Migrate from Cargoes Flow
A client built for Cargoes Flow can talk to Dockflow by changing two values: the base URL and the API key.
There is no public specification to test this surface against. The first migration runs together with Dockflow and validates the shapes on this page, including the event codes and the webhook body. Contact [email protected] to plan it.
What to change
| Setting | Before | After |
|---|---|---|
| Base URL | Your Cargoes Flow host | https://api.dockflow.com/compat/cargoes-flow |
X-DPW-ApiKey | Your Cargoes Flow key | A Dockflow API key |
X-DPW-Org-Token | Your organisation token | Accepted and ignored. It can stay. |
The path /flow/api/public_tracking/v1 also works under that base, so a client that only swaps the host needs no path change.
Get a token
- Sign in to the API Console and open API Keys.
- Click Create Key, choose the Production environment and the Downstream scope.
- Copy the key. It is shown only once.
See Authorization for the key format and for revoking a key.
What is covered
| Route | What it does |
|---|---|
GET /shipments | A bare array. Filters: shipmentType, status, shipmentNumber, mblNumber, containerNumber, awbNumber, search, includeUniqueContainers, _page, _limit. |
POST /createShipments | formData rows with uploadType: FORM_BY_MBL_NUMBER start tracking. |
PUT /updateShipments | Sends shipper, consignee and extra fields to an existing shipment. |
POST /uploadDocument | Multipart files and shipmentNumber. |
GET /mapRoutes?shipmentNumber= | The legs as routes. Empty until a plan exists. |
GET /carrierList | The carriers Dockflow knows. |
There is no delete route.
_page starts at 1. _limit defaults to 20, at most 100. The newest shipment comes first. search is an exact match on bill of lading, booking, container, reference or shipment number.
One row per shipment or per container
One master bill of lading is one Dockflow tradeflow, with the reference CARGOES-FLOW-<MBL> and the shipment number DF<id>.
- Without
includeUniqueContainers: one row per shipment.containerNumberis the first box andcontainerNumberslists all boxes. - With
includeUniqueContainers, or with acontainerNumberfilter: one row per box. A row has its own events plus the events no box owns, and every row keeps the sameshipmentNumber.
_limit counts shipments, so a page can hold more rows than _limit when it is expanded per box.
Answers and errors
| Case | Answer |
|---|---|
| Create or update succeeds | 200 with result: SUCCESS and errorDetail: []. A create adds shipments with shipmentNumber, mblNumber and created. |
| A row is refused | 200 with result: FAILED and errorDetail[].error. Rows are all or nothing: one bad row means nothing is created or updated. |
| A body that is not JSON, or an invalid list parameter | 400 with result: FAILED. |
mapRoutes for an unknown shipment | 404 with result: FAILED. |
| A refused key | The Dockflow body {"error": "...", "status": 401}. See Authorization. |
Field mapping
Create and update
| Field | Dockflow |
|---|---|
mblNumber | Bill of lading reference. |
bookingNumber | Booking reference. |
incoterm | Incoterms. |
oceanLine | Carrier SCAC when it is four letters, carrier name otherwise. |
shipper, consignee | Partners with that role. |
shipmentReference, promisedEta, promisedEtd, shipmentTags, demurrage fields | Custom references. They never feed the transport plan. GET /shipments reads promisedEta and promisedEtd back. |
Events
| Dockflow code | code |
|---|---|
GE | gateOutEmpty |
GN | gateInFull |
AE | loadedOnVessel |
VD | vesselDeparture, or vesselDepartureWithContainer when the event names a container |
VA | vesselArrival, or vesselArrivalWithContainer when the event names a container |
UV | dischargeFromVessel |
GT | gateOutFull |
GD | delivered |
GR | gateInEmpty |
At a transshipment port the code stays the same and the name adds "at transshipment". Any other Dockflow event keeps its own code and message, with isCustomEvent: true.
Status
statusisACTIVEfor an active shipment andCOMPLETEDotherwise.subStatus1comes from the latest actual event of the table:AT_ORIGIN,IN_TRANSIT,AT_DESTINATION,DELIVEREDorEMPTY_RETURNED. A transshipment event isIN_TRANSIT.subStatus2is the code of that event.shipmentTypeisAIR_SHIPMENTfor a shipment with air events andINTERMODAL_SHIPMENTotherwise.
Port fields such as firstPort, lastPort and an event's location carry UN/LOCODEs. origin and destination on a segment carry names.
Fields Dockflow adds
shipmentType, containerNumbers, awbNumber, shipmentEvents[].containerNumber, and on each segment originPortCode, originLatitude, originLongitude, destinationLatitude and destinationLongitude.
What stays empty
| Field | Value |
|---|---|
emissions.co2e.value | null. |
shipmentLegs.road | {}. |
promisedEta, promisedEtd | null in webhook bodies and on the upstream feed. Only GET /shipments fills them. |
loadingPort, dischargePort | null when no call carries that port role. |
segments[].transportMode | null on a leg before the port of loading or after the port of discharge, and on a leg with no known vessel. |
carrierScac, bookingNumber, event fields | null where Dockflow holds no value. |
In carrierList, supportsTrackByMbl and requiresMbl are true and supportsTrackByBookingNumber is false, because create accepts only a bill of lading. All rows are INTERMODAL_SHIPMENT.
Webhooks
Contact [email protected] to add a destination in the cargoes-flow format. You receive its signing secret once. It cannot be read again.
Each delivery is one POST whose body is the shipment object of GET /shipments, without the promised dates. The first migration validates this body. Retries, timeouts and ordering follow Webhook delivery.
| Header | Value |
|---|---|
X-Dockflow-Signature | sha256=<hex>: HMAC-SHA256 of the exact body with your signing secret. |
X-Dockflow-Timestamp | Unix 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 = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
How to test
- Create a key with the UAT environment and the Downstream scope. It works on
https://uat.dockflow.com/compat/cargoes-flowand writes to your UAT organisation. A real number is really tracked there. - Create a shipment:
curl -X POST https://uat.dockflow.com/compat/cargoes-flow/createShipments \
-H "X-DPW-ApiKey: uat_acme-logistics_Zk3n1c0Q2m9..." \
-H "Content-Type: application/json" \
-d '{"uploadType":"FORM_BY_MBL_NUMBER","formData":[{"mblNumber":"ANR00001010","oceanLine":"MAEU"}]}'
- Read it with
GET /shipments?shipmentNumber=DF74123, using the number from the answer. The import runs after the answer, so the first read can be short. - To test a webhook, open the destination in the console and click Send test with the Live tradeflow scenario, naming the reference
CARGOES-FLOW-<MBL>of a tradeflow that has data. The Ocean basic scenario is refused for this format.
Polling is also possible: GET /v3/upstream?format=cargoes-flow with an Upstream key returns these records. See Upstream filters.