Skip to main content

Migrate from ShipsGo

A client built for the ShipsGo v2 ocean API can talk to Dockflow by changing two values. The paths, the request bodies and the response shapes stay as they are.

The responses and error bodies on this surface are checked by contract tests against ShipsGo's public OpenAPI document.

What to change​

SettingBeforeAfter
Base URLYour ShipsGo v2 base URLhttps://api.dockflow.com/compat/shipsgo/v2
X-Shipsgo-User-TokenYour ShipsGo tokenA Dockflow API key

The header name does not change.

Get a token​

  1. Sign in to the API Console and open API Keys.
  2. Click Create Key, choose the Production environment and the Downstream scope.
  3. Copy the key. It is shown only once.

See Authorization for the key format and for revoking a key.

What is covered​

RouteWhat it does
POST /ocean/shipmentsStarts tracking. Answers 200 with message: SUCCESS and the short shipment. A shipment that exists already answers 409 with message: ALREADY_EXISTS and that shipment.
GET /ocean/shipmentsThe list, with skip and take (default 25, at most 100). meta.more says if another page follows, and meta.total is null while it does.
GET /ocean/shipments/{id}One shipment, with its containers and movements.
PATCH /ocean/shipments/{id}Changes reference.
DELETE /ocean/shipments/{id}Stops tracking and removes the shipment.
GET /ocean/carriersThe carriers Dockflow knows, with skip and take. Filters: filters[status]=eq: and filters[name]=contains:. Every carrier reports status: ACTIVE.

POST and DELETE on followers and tags answer 403 with message: FORBIDDEN. GET /ocean/shipments/{id}/geojson answers 404. Air routes are not part of this surface.

Identity​

The duplicate rule is the client reference plus the booking number, or plus the container number when there is no booking. Dockflow stores this pair as the tradeflow reference: <reference>~<NUMBER>, or SHIPSGO-<NUMBER> without a client reference. This stored form is what your colleagues see on the tradeflow in Dockflow.

A duplicate POST still sends its identifiers to the import, so it can add a container to the existing shipment.

Errors​

StatusmessageWhen
401UNAUTHORIZEDThe token is missing, malformed or unknown.
403FORBIDDENThe token is revoked or lacks the scope, or a PATCH or DELETE touches a shipment another organisation created.
404NOT_FOUNDNo shipment with this id belongs to your organisation.
409ALREADY_EXISTSSee above.
422VALIDATION_FAILEDThe body holds errors, a map of field to problem.

Field mapping​

Create​

ShipsGo fieldDockflow
referenceThe first part of the tradeflow reference. 5 to 128 characters.
booking_numberBooking reference. A master bill of lading sent in this field is also stored as a booking reference.
container_numberContainer reference.
carrierPassed on as the carrier name of that SCAC. An unknown SCAC or an SG_ code is accepted and not passed on.
followers, tagsValidated and dropped. No follower mail is sent.

Events​

Dockflow codeevent
GEEMSH
GNGTIN
AELOAD
VDDEPA
VAARRV
UVDISC
GT, GDGTOT
GREMRT

A movement has status: ACT when it happened and status: EST when it is expected. Dockflow events with other codes are left out.

Shipment status​

statusWhen
UNTRACKEDInactive and no events.
NEWActive, with no events, no plan, no containers and no update yet.
INPROGRESSActive with no events, once a plan, a container or an update exists.
DISCHARGEDAn actual empty return, or an actual discharge, gate out or delivery at the port of discharge.
ARRIVEDAn actual vessel arrival at the port of discharge.
SAILINGAn actual departure, or an actual arrival or discharge at another port.
LOADEDAn actual load.
BOOKEDEvents exist, none of the above.

Container status​

A container takes the status of its latest actual movement: EMPTY_SHIPPER, GATE_IN, LOADED, SAILING, ARRIVED, DISCHARGED, GATE_OUT or EMPTY_RETURN. An arrival or discharge away from the port of discharge reads SAILING. Without an actual movement the status is UNKNOWN.

type is HC for an ISO high cube general purpose code, and the ISO code otherwise.

What stays empty​

FieldValue
tokens{}. Dockflow has no map link.
creator{}.
followers, tagsAlways [].
checked_at, route.co2_emissionnull.
routenull until the plan knows a port of loading and a port of discharge.
route.port_of_loading.date_of_loading_initial, route.port_of_discharge.date_of_discharge_initialAbsent when no event carries them.
route.port_of_discharge.date_of_discharge_predictednull when no event carries it.
event.triggered_by.emailAbsent.
X-Shipsgo-Credits-*, X-RateLimit-* response headersAbsent.

booking_number and container_number show only the identifying number until the import has run. discarded_at holds the last change of an inactive shipment, because Dockflow stores no separate deactivation time.

What behaves differently​

  • List filters other than filters[id]=eq: and filters[container_number]=eq: answer 422. Every order_by answers 422.
  • The list is ordered by last change, oldest first.
  • Only the OCEAN.SHIPMENTS.SHIPMENT_UPDATED webhook exists.

Webhooks​

Webhook destinations are set up on the Dockflow side. Contact [email protected] to add a destination in the shipsgo-v2 format. You receive its signing secret once. It cannot be read again.

Each delivery is one POST with this body:

{
"event": {
"id": "6f1c2b7a-9d3e-4c10-8a55-1e2f3a4b5c6d",
"name": "OCEAN.SHIPMENTS.SHIPMENT_UPDATED",
"triggered_by": { "name": "Dockflow" }
},
"shipment": { "id": 74123, "status": "SAILING" }
}

shipment is the full object that GET /ocean/shipments/{id} returns; the example shortens it. The event id is the same for a redelivery of the same body. Retries, timeouts and ordering follow Webhook delivery.

HeaderValue
X-Shipsgo-Webhook-IdThe id of the destination.
X-Shipsgo-Webhook-NameThe slug of the destination, not its display name.
X-Shipsgo-Webhook-SignatureHMAC-SHA256 of the body with your signing secret, as bare hex.
X-Dockflow-SignatureThe same value as sha256=<hex>.
X-Dockflow-TimestampUnix seconds. It is not signed, so it gives no replay protection.

Verify the signature​

Compute the HMAC over the exact bytes you received, before any JSON parsing:

import hashlib, hmac

def is_authentic(raw_body: bytes, header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)

Pass the value of X-Shipsgo-Webhook-Signature as header. For X-Dockflow-Signature, compare against "sha256=" + expected. An existing ShipsGo verification keeps working when it computes the same bare hex value.

How to test​

  1. Create a key with the UAT environment and the Downstream scope. It works on https://uat.dockflow.com/compat/shipsgo/v2 and writes to your UAT organisation. A real number is really tracked there.
  2. Create a shipment:
curl -X POST https://uat.dockflow.com/compat/shipsgo/v2/ocean/shipments \
-H "X-Shipsgo-User-Token: uat_acme-logistics_Zk3n1c0Q2m9..." \
-H "Content-Type: application/json" \
-d '{"reference":"PO-4564268","booking_number":"94512540"}'
  1. Read it with the id from the answer. The import runs after the answer, so status starts at NEW.
curl https://uat.dockflow.com/compat/shipsgo/v2/ocean/shipments/74123 \
-H "X-Shipsgo-User-Token: uat_acme-logistics_Zk3n1c0Q2m9..."
  1. To test a webhook, open the destination in the console and click Send test with the Live tradeflow scenario, naming the stored reference of a tradeflow that has data. The Ocean basic scenario is refused for this format.

Polling is also possible: GET /v3/upstream?format=shipsgo-v2 with an Upstream key returns these same shipment objects. See Upstream filters.

Was this page helpful?