Skip to main content

Migrate from project44

A client built for project44's v4 ocean tracking 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 project44's public API document.

What to change​

SettingBeforeAfter
Base URLYour project44 base URLhttps://api.dockflow.com/compat/project44
Client secretYour project44 client secretA Dockflow API key

The client_id can stay. Dockflow only needs it to be present.

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.

POST /api/v4/oauth2/token accepts a form body, a JSON body or Basic authentication, with grant_type=client_credentials. It returns your secret as access_token, with token_type: BEARER and expires_in: 3600. The exchange does not check the secret. The first real call does, and the key itself does not expire on that clock.

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

What is covered​

All paths are under /api/v4.

RouteWhat it does
POST /oauth2/tokenThe token exchange above.
POST /shipments/trackingStarts tracking. With id in the body it adds identifiers to that shipment. Answers 200 with the described shipment.
PUT /shipments/trackingAs POST, and the list you send replaces the stored list for each identifier class you name: bills of lading, bookings or containers.
GET /shipments/{id}/trackingThe shipment.
GET /shipments/{id}/tracking/historyShipment, events, states, positions. This is also the webhook body.
GET /shipments/{id}/eventhistoryEvents and states. Add includeRouteInfo=true for the route.
GET /shipments/{id}/positionhistoryPositions, which are empty. Add includeRouteInfo=true for the route.
GET /shipments/{id}/attributesAn empty list.
DELETE /shipments/{id}/tracking202. A parent id stops tracking and removes the shipment. A container id removes that container.
Any /webhooks path403. Destinations are set outside this API, see Webhooks.

Not part of this surface: /api/v4/ocean/shipments, /services/unified-search, loads and carrier push paths. There is no read by identifier: read by id.

Identity​

One tracked object is one Dockflow tradeflow. The same set of tracking identifiers reaches the same shipment. The shipment id is a UUID derived from the tradeflow, and each container has its own child id that works on the read and delete routes.

Errors​

Errors use the ApiError shape: httpStatusCode, httpMessage, errorMessage and errors. A token refusal has the same shape, with 401 or 403. The token route uses error and error_description.

Field mapping​

Identifiers you send​

TypeDockflow
BILL_OF_LADINGBill of lading reference.
BOOKING_NUMBERBooking reference.
CONTAINER_ID, relatedShipmentsContainer reference. CONTAINER_NUMBER is accepted as an alias.
CARRIER_SCACCarrier SCAC. No SCAC is required next to a bill or booking.
FFW_SCACAccepted. It is only repeated in the answer to the same call.
HOUSE_BILL_OF_LADING, TRACKING_NUMBERStored and repeated in the answer to the create. Later reads do not show them, and they do not start tracking by themselves.

Other types answer 400.

Stops​

Dockflow roletype
Port of loadingPORT_OF_LOADING
Port of dischargePORT_OF_DISCHARGE
TransshipmentTRANSSHIPMENT_PORT
Before the port of loadingORIGIN
After the port of dischargeDESTINATION
Anything elseOTHER

Events​

Dockflow codetype
GEGATE_OUT_EMPTY
GNGATE_IN_FULL
AELOAD
VDDEPARTURE_FROM_STOP
VAARRIVAL_AT_STOP
UVDISCHARGE
GT, GDGATE_OUT_FULL
GRGATE_IN_EMPTY

details.ocean.code is filled when the event names its port role, for example VESSEL_DEPARTURE_FROM_TRANSSHIPMENT_PORT. When Dockflow does not know the role, the field is absent.

dateTimes holds PLANNED, ESTIMATE and ACTUAL entries, with selected on the one the event shows. A container on an event sits in dateTimes[].sourceIdentifiers as CONTAINER_ID. Identical events of several containers are one parent event that names them all, and a child read shows only its own. source is CARRIER or UNKNOWN.

Other Dockflow events, such as customs events, are left out.

Segments and states​

A route segment is OCEAN unless the departure or arrival event states BARGE, RAIL or TRUCKLOAD. A state is in transit between an actual departure and arrival, and AT_STOP between an actual arrival and the next departure.

What stays empty​

FieldValue
positions, costs, contents, exceptions[].
attributes, accessGroups, plan, shipmentShareLink, sharingContext, sensorContext, emissionsAbsent.
receivedDateTime, estimateLastCalculatedDateTime, plannedEndDateTimeAbsent.
location.masterLocationId, street addressAbsent.
Terminal nameNo field carries it.

routeInfo, events, plan, attributes, accessGroups, costs, contents and shipmentShareLink in a request are not applied. The answer says so in problems.

A tradeflow with several shipments answers reads from its first shipment. Webhooks deliver one body per shipment, under the same id.

Webhooks​

The /webhooks routes answer 403. Contact [email protected] to add a destination in the project44-v4 format. You receive its signing secret once. It cannot be read again.

Each delivery is one POST whose body has the shape of GET /shipments/{id}/tracking/history: shipment, events, positions, states, costs, contents and exceptions. Retries, timeouts and ordering follow Webhook delivery.

HeaderValue
X-Dockflow-Signaturesha256=<hex>: HMAC-SHA256 of the exact body with your signing secret.
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 = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)

How to test​

  1. Create a key with the UAT environment and the Downstream scope. It works on https://uat.dockflow.com/compat/project44 and writes to your UAT organisation. A real number is really tracked there.
  2. Exchange and create:
curl -X POST https://uat.dockflow.com/compat/project44/api/v4/oauth2/token \
-d grant_type=client_credentials -d client_id=acme \
-d client_secret=uat_acme-logistics_Zk3n1c0Q2m9...

curl -X POST https://uat.dockflow.com/compat/project44/api/v4/shipments/tracking \
-H "Authorization: Bearer uat_acme-logistics_Zk3n1c0Q2m9..." \
-H "Content-Type: application/json" \
-d '{"identifiers":[{"type":"BILL_OF_LADING","value":"ANR00001010"},{"type":"CARRIER_SCAC","value":"MAEU"}]}'
  1. Read GET /api/v4/shipments/{id}/tracking/history with the id from the answer. The import runs after the answer, so the first read can be short.
  2. 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=project44-v4 with an Upstream key returns these same history objects. See Upstream filters.

Was this page helpful?