Shipments
/{connectorId}/shipmentscreateShipmentCreate a shipment
Submits a booking to the carrier. On success, returns an `orderNumber` and `consignmentNumber` (if assigned immediately by the carrier). **Collection time rules (TNT AU):** `collectionDateTime` must be 07:00–17:00, `collectionCloseTime` must be 13:00–19:00, and the window between them must be ≥ 120 minutes — all evaluated as wall-clock time local to the `collection` address, not a fixed national zone (a WA collection is checked against WA local time, not AEST). Violations return 400 with a structured `errors[]` list.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| connectorId | path | ✓ | Database ID of the carrier connector account (from `GET /connectors`) |
Request body
ConsignmentBookingRequest
Responses
201Booking created successfullyBookingResponse
400Invalid request — validation failed, the carrier rejected the input, or `rateId` could not be linked (`code: QUOTE_NOT_FOUND`: the rate does not exist for this tenant, connector and `serviceCode`, or does not belong to the supplied `quoteId`). Nothing is booked at the carrier when `QUOTE_NOT_FOUND` is returned.Error
401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
404Connector not found or not active for this tenantError
410`code: QUOTE_EXPIRED` — the quote that `rateId` belongs to is more than 48 hours old and can no longer be linked to a booking. Nothing is booked at the carrier; request fresh rates and book with the new `rateId`.Error
429Too many requests for this API key — see Retry-After headerError
502Carrier API returned an error or is unavailableError
/shipmentslistShipmentsList shipments
Returns shipments for the authenticated tenant, sorted by `createdAt` (most recent first unless `sortDirection=asc`). Poll this endpoint to detect when `consignmentNumber` becomes available for carriers that assign it asynchronously. **Pagination:** `offset` is an exact row skip — `offset=30&limit=50` returns rows 31–80 of the filtered, sorted result. `total` counts every shipment matching the filters. The response echoes the effective `limit` and `offset`. **Filters** combine with AND. `createdFrom` is inclusive and `createdTo` exclusive; a bare date (`YYYY-MM-DD`) means 00:00:00Z that day. **Scope:** requires `bookings:read` (implied by `bookings:write`). Invalid query parameters return 400 with `errors[]` naming each field.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| limit | query | — | Number of shipments to return (1–200, default 50). Non-numeric values are rejected with 400. |
| offset | query | — | Exact number of shipments to skip (default 0). Non-numeric values are rejected with 400. |
| status | query | — | Only shipments with this status |
| carrier | query | — | Comma-separated carriers, e.g. `TNT_AU,TGE_AU` |
| source | query | — | Only shipments booked via the API or the portal |
| createdFrom | query | — | Inclusive lower bound on `createdAt`. ISO 8601 date (`YYYY-MM-DD`, meaning 00:00:00Z) or date-time with `Z` or an offset. |
| createdTo | query | — | Exclusive upper bound on `createdAt`. Same formats as `createdFrom`; must be after it. |
| delivered | query | — | `true` returns only shipments with a recorded `deliveredAt`. `false` is not supported yet (400) — omit the parameter to include undelivered shipments. |
| customerReference | query | — | Exact match on `customerReference` (max 100 characters) |
| search | query | — | Case-insensitive substring match across order number, consignment number, carrier booking reference, customer reference, receiver company, destination suburb and destination postcode (max 200 characters) |
| sortDirection | query | — | Sort direction on `createdAt` (default `desc`) |
Responses
200Shipment listShipmentListResponse
400Invalid query parameterError
401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
429Too many requests for this API key — see Retry-After headerError
/shipments/{id}getShipmentGet shipment detail
Returns full detail for a single shipment including sender, collection, and receiver addresses, item dimensions, and booking metadata. Requires `bookings:read` (implied by `bookings:write`).
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ | Datashift shipment ID (from booking response or `GET /shipments`) |
Responses
200Shipment detailShipmentDetailResponse
401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
404Connector not found or not active for this tenantError
429Too many requests for this API key — see Retry-After headerError
/tracktrackByConsignmentTrack by consignment number
Returns live tracking information for a shipment identified by its carrier consignment number. **Lookup strategy:** 1. If `carrier` is given, queries that carrier's connector directly — no shipment lookup, no fan-out. Use this when you already know the carrier (e.g. from your own shipment record) for a faster, unambiguous result. 2. Otherwise, searches your booked shipments for a matching `consignmentNumber`. If found, queries that carrier directly. 3. If no match in your shipments and no `carrier` given, fans out to all active carrier connectors and returns the first non-null result. **Carrier support:** TNT AU, BEX AU, and TGE AU all support tracking. TNT AU and TGE AU return proof-of-delivery (`pod`) on a delivered shipment (TGE: `signedBy` only) — see the `TrackingInfo` schema. **Polling:** Some carriers (BEX AU) process consignments asynchronously after booking. If tracking returns `supported: true` but `tracking: null`, wait 30–60 seconds and retry.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| consignment | query | ✓ | Carrier consignment number to look up (e.g. the `consignmentNumber` from a booking response) |
| carrier | query | — | Optional. Scopes the lookup to a single carrier, skipping the shipment-match and fan-out steps. Recommended when you already know the carrier. |
Responses
200Tracking result. Check `supported` — if false the carrier does not expose a tracking API.TrackingResponse
400Missing `consignment` query parameter, or `carrier` is not a recognized carrierError
401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
404Consignment not found in your shipments and no active carrier returned tracking data, or (with `carrier` given) no active connector exists for that carrierError
429Too many requests for this API key — see Retry-After headerError
/shipments/{id}/labelgetShipmentLabelGet shipment label
Returns the PDF shipping label for a booked shipment. **Label availability:** Most carriers (including TNT AU) generate the label at booking time. If the label is not yet available, this endpoint returns 404 with `code: LABEL_NOT_AVAILABLE` — poll with exponential backoff until it becomes available. **Response format:** `application/pdf`. Save as a file or pipe directly to a label printer.
Parameters
| Name | In | Required | Description |
|---|---|---|---|
| id | path | ✓ | Datashift shipment ID (from booking response or `GET /shipments`) |
Responses
200PDF label401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
404Shipment not found, or label not yet available from the carrier. Check `code` — `LABEL_NOT_AVAILABLE` means retry; anything else means the shipment ID is wrong.LabelNotAvailableError
409Shipment has been cancelled in DataShift — no label is served for a cancelled shipment.Error
429Too many requests for this API key — see Retry-After headerError
/shipments/cancel/eligibilitycheckShipmentCancelEligibilityCheck shipment cancel eligibility
**DataShift-only cancellation — read this first.** None of DataShift's carriers (TNT AU, BEX AU (Border Express) and TGE AU) expose a cancel API today. Cancelling a shipment through DataShift only marks it cancelled in DataShift — the carrier is never called or notified, and `carrierNotified` in every response from this API is always `false`. If the label has already been printed and handed to the carrier (or is likely to be, e.g. it's on a manifest or a pickup has been booked), destroy it — cancelling here does not stop the carrier from collecting or invoicing the freight. Pure read — this endpoint never cancels anything. Call it before `POST /shipments/cancel` to show the caller what will happen: which ids are blocked (and why) and which will succeed with a warning. **Scope:** requires `bookings:read` or `bookings:cancel`. **Blocking reasons** (`results[].code` when `eligible: false`): `NOT_FOUND`, `ALREADY_CANCELLED`, `DELIVERED`, `IN_CARRIER_NETWORK` (collected, tracking-scanned, or already in transit / out for delivery / exception). **Warnings** (`results[].warnings[].code`, stack independently, only on an eligible result): `MANIFESTED` (already sent to the carrier on a manifest — expect a charge; cancelling anyway requires `acknowledgeManifestedIds` containing its id, or `acknowledgeManifested: true`, on the actual cancel call), `PICKUP_BOOKED` (a pickup is booked; informational only, some carriers auto-book pickups so this doesn't block). An id that doesn't belong to your tenant (or doesn't exist at all) comes back exactly like any other blocked id — `{ eligible: false, code: 'NOT_FOUND' }` — never a 404, so this can't be used to probe whether an id exists in another tenant.
Request body
CancelEligibilityRequest
Responses
200Per-id eligibility, in the same (deduped) order as the requestCancelEligibilityResponse
400Invalid request — validation failed or carrier rejected inputError
401Missing or invalid API keyError
403Freight access is not active for this tenant, or the API key lacks the scope this endpoint requiresError
429Too many requests for this API key — see Retry-After headerError
/shipments/cancelcancelShipmentsCancel shipments (DataShift only)
**DataShift-only cancellation.** No carrier is called or notified — see `POST /shipments/cancel/eligibility` above for the full caveat. `carrierNotified` is always `false`. Requires the `bookings:cancel` scope specifically; `bookings:write` alone does not grant it. Runs eligibility fresh for every id (not trusting a stale client-side check). A manifested shipment is skipped with `MANIFEST_ACK_REQUIRED` unless its id is in `acknowledgeManifestedIds` or `acknowledgeManifested: true` is given. Prefer `acknowledgeManifestedIds`: it covers only the shipments you checked, so one manifested after your eligibility check is skipped rather than cancelled unseen. **Required acknowledgement:** `acknowledgeCarrierNotNotified` must be exactly `true`. Missing or `false` returns 422 `CARRIER_ACK_REQUIRED` — distinct from a generic validation 400 so you can prompt specifically for that acknowledgement. **Response:** `cancelled` (ids actually cancelled), `skipped` (ids that weren't, each with a `code` — the same blocking codes as the eligibility endpoint, plus `MANIFEST_ACK_REQUIRED`), `carrierNotified: false`, and `warning` (always present, the DataShift-only caveat as text — surface it to the end user). `manifestedWarning` is present only when at least one cancelled shipment had already been manifested.
Request body
CancelShipmentsRequest
Responses
200Cancel outcome — check `skipped` for any ids that were not cancelledCancelShipmentsResponse
400Invalid request — validation failed or carrier rejected inputError
401Missing or invalid API keyError
403Freight access not active for this tenant, or the API key lacks the `bookings:cancel` scope (note: `bookings:write` alone is not sufficient)Error
422Missing or false `acknowledgeCarrierNotNotified`Error
429Too many requests for this API key — see Retry-After headerError
