DataShift Logo

Datashift Freight API · v0.1.0

API Reference

Carrier-agnostic freight orchestration. Rate-shop and book shipments across all your configured carrier accounts from a single API.

Base URL: https://datashift.com.au/api/freightAuth: Authorization: Bearer dsfrt_<64-hex-chars>

Connectors

get/connectorslistConnectors

List active connectors

Returns all active carrier connectors for the authenticated tenant. Use the `id` values as `connectorId` in rate and booking requests. Credentials are never included in the response.

Responses

200Active connectors for this tenant
ConnectorListResponse
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error

Rates

post/ratesgetAllRates

Get rates from all active connectors

Fans out to every active carrier connector for the authenticated tenant and returns all available services. Uses `Promise.allSettled` — one carrier being down does not fail the request; its `error` entry is included in the response instead.

Request body

RateRequest

Responses

200Rate results from all connectors. Each entry is either a successful carrier result or an error — check `success` to distinguish.
AggregatedRateResponse
400Invalid request — validation failed or carrier rejected input
Error
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
post/{connectorId}/ratesgetConnectorRates

Get rates from a specific connector

Queries a single carrier connector. Set `serviceCode` to get pricing for one service only; omit it to receive all available services.

Parameters

NameInRequiredDescription
connectorIdpathDatabase ID of the carrier connector account (from `GET /connectors`)

Request body

RateRequest

Responses

200Available rated services for this connector
RatedService[]
400Invalid request — validation failed or carrier rejected input
Error
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Connector not found or not active for this tenant
Error
502Carrier API returned an error or is unavailable
Error

Shipments

post/{connectorId}/shipmentscreateShipment

Create 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 AEST, `collectionCloseTime` must be 13:00–19:00 AEST, and the window between them must be ≥ 120 minutes. Violations return 400 with a structured `errors[]` list.

Parameters

NameInRequiredDescription
connectorIdpathDatabase ID of the carrier connector account (from `GET /connectors`)

Request body

BookingRequest

Responses

201Booking created successfully
BookingResponse
400Invalid request — validation failed or carrier rejected input
Error
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Connector not found or not active for this tenant
Error
502Carrier API returned an error or is unavailable
Error
get/shipmentslistShipments

List shipments

Returns shipments for the authenticated tenant, most recent first. Use `limit` and `offset` to page through results. Poll this endpoint to detect when `consignmentNumber` becomes available for carriers that assign it asynchronously.

Parameters

NameInRequiredDescription
limitqueryNumber of shipments to return (1–200, default 50)
offsetqueryNumber of shipments to skip for pagination (default 0)

Responses

200Shipment list
ShipmentListResponse
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
get/shipments/{id}getShipment

Get shipment detail

Returns full detail for a single shipment including sender, collection, and receiver addresses, item dimensions, and booking metadata.

Parameters

NameInRequiredDescription
idpathDatashift shipment ID (from booking response or `GET /shipments`)

Responses

200Shipment detail
ShipmentDetailResponse
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Connector not found or not active for this tenant
Error
get/tracktrackByConsignment

Track 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:** Both TNT AU and BEX AU support tracking. **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

NameInRequiredDescription
consignmentqueryCarrier consignment number to look up (e.g. the `consignmentNumber` from a booking response)
carrierqueryOptional. 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 carrier
Error
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Consignment not found in your shipments and no active carrier returned tracking data, or (with `carrier` given) no active connector exists for that carrier
Error
get/shipments/{id}/labelgetShipmentLabel

Get 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

NameInRequiredDescription
idpathDatashift shipment ID (from booking response or `GET /shipments`)

Responses

200PDF label
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
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

Pickups

post/{connectorId}/pickupscreatePickupBooking

Book a pickup

Books a pickup — either derived from an existing consignment (`shipmentId`, address/items locked to that consignment) or standalone (`collection`, `items`, and `receiver` supplied directly, all three required together). A partial standalone submission (e.g. `collection` without `items`/`receiver`) is rejected with a 400.

Parameters

NameInRequiredDescription
connectorIdpathDatabase ID of the carrier connector account (from `GET /connectors`)

Responses

201Pickup booked successfully (or recorded as failed — check `status`)
PickupBookingResponse
400Invalid request — validation failed or carrier rejected input
Error
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Connector not found or not active for this tenant
Error
502Carrier API returned an error or is unavailable
Error
get/pickups/{id}getPickupBooking

Retrieve a pickup booking

Parameters

NameInRequiredDescription
idpath

Responses

200Pickup booking detail
PickupBookingDetailResponse
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
404Connector not found or not active for this tenant
Error

Printing

get/print-deviceslistPrintDevices

List print devices

Returns active (non-revoked) Datashift print devices registered for the tenant. Use the `id` of the desired device as `deviceId` when calling `POST /shipments/{id}/print`. Devices are registered from the Datashift portal (Settings → Freight → Print Devices). Each device runs the Datashift label client software and listens for print jobs over Pub/Sub. Revoked devices are excluded from this list.

Responses

200Active print devices for this tenant
PrintDeviceListResponse
401Missing or invalid API key
Error
403Freight access is not active for this tenant
Error
post/shipments/{id}/printprintShipmentLabel

Print shipment label

Fetches the shipment label from the carrier and queues a print job to the specified device. **Prerequisites:** - Direct print must be enabled for the tenant in Settings → Freight. - The target device must be registered, active, and running the Datashift label client. **Typical flow:** Call `GET /print-devices` once during setup, store each device's `id` in your WMS/ERP config keyed by workstation, then pass the relevant `deviceId` in this request after booking. **Handling `DEVICE_NOT_FOUND`:** If the response is 404 with `code: DEVICE_NOT_FOUND`, the stored device ID is stale (device was revoked or replaced). Re-fetch `GET /print-devices`, overwrite your stored device list, and prompt the user to select a device before retrying. **Handling `LABEL_NOT_AVAILABLE`:** The label is not yet ready from the carrier — poll `GET /shipments/{id}/label` with exponential backoff and retry once available.

Parameters

NameInRequiredDescription
idpathDatashift shipment ID (from booking response or `GET /shipments`)

Request body

PrintRequest

Responses

201Print job queued successfully
PrintJobCreated
400Invalid request — validation failed or carrier rejected input
Error
401Missing or invalid API key
Error
403Freight access not active, or direct print not enabled for this tenant
Error
404Shipment or device not found, or label not yet available
PrintNotFoundError

Schemas

ConnectorSummary

{
  id: string  // Use as `connectorId` in rate and booking requests
  carrier: TNT_AU | BEX_AU
  name: string  // Human-readable label for this connector
  services: {
    serviceCode: string
    description: string
  }[]  // Available services for this connector. Populated after the first successful connection test. Carriers with a static catalog (BEX AU) always return services; dynamic carriers (TNT AU) return an empty array until tested.
}

ConnectorListResponse

{
  connectors: ConnectorSummary[]
}

Address

{
  suburb: string
  postcode: string
  state: string
  residential: boolean  // Whether this is a residential address. Triggers a residential surcharge on collection (RSP) or delivery (RSD) where applicable.
}

BookingAddress

{
  companyName: string
  contactName?: string
  phoneAreaCode?: string
  phoneNumber?: string
  address1: string
  address2?: string
  suburb: string
  state: string
  postcode: string
  email?: string  // Receiver email address for carrier delivery notifications (BEX AU)
}

Package

{
  count: integer
  lengthCm: number
  widthCm: number
  heightCm: number
  weightKg: number
}

RateRequest

{
  from: Address
  to: Address
  packages: Package[]
  dangerousGoods: boolean
  date?: string  // Shipping date (YYYY-MM-DD). Defaults to today in AEST if omitted.
  localDateTime?: string  // Current local time in AEST (yyyy-MM-ddTHH:mm:ss, no TZ suffix). Used by TNT to determine booking cutoff eligibility. Defaults to current AEST time if omitted.
  serviceCode?: string | null  // If set, returns pricing for this service only. Omit (or pass null) to receive all available services.
  isDocument?: boolean  // Mark as a document shipment (TNT AU — affects packaging type and commodity code)
}

RatedService

{
  serviceCode: string
  description: string
  price: number
  currency: string
  eta?: string | null  // Estimated delivery datetime (ISO 8601, no TZ suffix)
  transitDays?: integer | null  // Transit duration in whole days, for carriers (e.g. TGE AU) whose rate response gives a duration rather than a delivery date. Null when `eta` is supplied directly instead.
  bookingCutoff: string  // Latest time to book for this service today (HH:mm:ss)
  collectionCutoff: string  // Latest time for courier collection today (HH:mm:ss)
  priceGst?: number  // GST component of the price (BEX AU only)
  priceIncGst?: number  // Total price including GST (BEX AU only)
}

AggregatedRateResult

{
  connectorId: string
  carrier: string
  success: boolean
  rates?: RatedService[]  // Present when success is true
  error?: CarrierError  // Present when success is false
}

AggregatedRateResponse

{
  results: AggregatedRateResult[]
}

Items

{
  count: integer
  totalWeightKg: number
  maxLengthCm: number
  maxWidthCm: number
  maxHeightCm: number
}

BookingRequest

{
  sender: BookingAddress
  collection: BookingAddress  // Collection address — often the same as sender but can differ (e.g. a warehouse pickup for an office-based account).
  receiver: BookingAddress
  items: Items
  packagingCode: CT | BX | SA | PA | EN | CR | DR | RO | BG | PC  // TNT AU packaging codes: `CT` — Carton, `BX` — Box, `SA` — Satchel, `PA` — Pallet, `EN` — Envelope
  serviceCode: string  // Carrier service code from a prior `getRates` call
  collectionDateTime: string  // Pickup ready time in AEST local time, no TZ suffix. Must be 07:00–17:00 AEST, ≥ 120 minutes before collectionCloseTime.
  collectionCloseTime: string  // Premises close time in AEST local time as hhmm string. Must be 13:00–19:00 AEST.
  payer: Sender | Receiver | ThirdParty
  payerAccountNumber?: string  // Account number to charge when `payer` is `Receiver` or `ThirdParty`. Not required when `payer` is `Sender` (the connector's own account is charged).
  thirdPartyAddress?: BookingAddress  // Required when `payer` is `ThirdParty` (TNT AU). The third-party account holder's address.
  dangerousGoods: boolean
  dangerousGoodsDetails?: {
    unNumber: string  // UN number (4 digits)
    packingGroup?: I | II | III  // Packing group (if applicable)
    hazClass: string  // Hazard class (e.g. `3`, `8`)
    packageType: string  // Package type description
    quantity: integer
    aggregateQuantity: integer
    shippingName: string  // Proper shipping name
    subRisk?: string  // Subsidiary risk (optional)
    contactName: string
    contactPhone: string
  }  // Required when `dangerousGoods` is true.
  customerReference?: string
  specialInstructions?: string
  alertAddresses?: {
    email: string
    phone?: string  // Mobile number for SMS alerts
    alertTypes: string[]  // Alert event types to subscribe to
  }[]  // TNT AU — email/SMS notifications for shipment events.
  keepInStore?: {
    numberOfDays: integer  // Number of business days to hold (1–5)
  }  // TNT AU — hold shipment at depot rather than attempting delivery.
  pickupBookingNumber?: string  // TNT AU — pre-arranged pickup booking reference
  extendedWarranty?: {
    value: number  // Declared value in AUD (max $10,000)
    class: A | B | C  // Warranty class tier
  }  // TNT AU — declare shipment value for extended warranty coverage.
  foodFreight?: boolean  // TNT AU — mark shipment as food freight
  bexAlerts?: {
    alertType: DESPATCHED | DELIVERY_STATUS | DELIVERED
    contactName: string
    emailAddress: string
  }[]  // BEX AU — email notifications for shipment events.
  timeSensitive?: {
    timeslot?: {
      dateTime: string  // Appointment datetime (ISO 8601)
      reference: string  // Appointment booking reference
    }  // Specific delivery appointment
    deliveryWindow?: {
      startDate: string
      endDate: string
      startTime?: string  // Earliest acceptable delivery time (HH:MM)
      endTime?: string  // Latest acceptable delivery time (HH:MM)
    }  // Date range delivery window
  }  // BEX AU — request a time-sensitive delivery. Provide either `timeslot` (specific appointment) or `deliveryWindow` (date range with optional time bounds).
  pallets?: {
    chep?: {
      exchangeIn?: integer
      exchangeOut?: integer
      directTransferToRec?: integer
      transferOffBex?: integer
      docketNumber?: string
    }
    loscam?: {
      exchangeIn?: integer
      exchangeOut?: integer
      directTransferToRec?: integer
      transferOffBex?: integer
      docketNumber?: string
    }
  }  // BEX AU — pallet exchange configuration for CHEP and/or LOSCAM.
  customerBarcodes?: string[]  // BEX AU — customer-supplied barcodes to associate with the consignment (max 50 chars each)
}

BookingResponse

{
  orderNumber: string  // Booking reference issued by the carrier.
  consignmentNumber?: string | null  // Consignment/tracking number. Present immediately for some carriers (e.g. TNT AU); null for carriers that issue it asynchronously.
  barcodes?: string[]  // Carrier-issued barcode numbers for individual items in the consignment (BEX AU only)
  shipmentId: string  // Datashift shipment ID — use with `GET /shipments/{id}`, `GET /shipments/{id}/label`, and `POST /shipments/{id}/print`
  connectorId: string
  pickupStatus?: NOT_BOOKED | BOOKED | FAILED  // Outcome of the connector's auto-book-pickup setting, if enabled. `NOT_BOOKED` if auto-booking is off — call POST /pickups to book one explicitly.
  labelWarning?: string  // Present when the pickup booking failed (see pickupStatus) or the carrier returned a non-fatal warning alongside the label.
}

PickupBookingRequest

{
  shipmentId?: string  // Derived mode — book against this existing consignment. Omit for standalone.
  pickupDate: string
  readyTime: string
  closeTime: string
  serviceCode?: string  // TNT only — ignored by BEX
  collection?: BookingAddress  // Standalone mode only — required together with items and receiver
  items?: Items  // Standalone mode only — required together with collection and receiver
  receiver?: BookingAddress  // Standalone mode only — required together with collection and items
}

PickupBookingResponse

{
  pickupBookingId?: string
  orderNumber?: string | null
  status?: BOOKED | FAILED
  connectorId?: string
}

PickupBookingDetail

{
  id?: string
  tenantId?: string
  connectorId?: string
  carrier?: TNT_AU | BEX_AU
  status?: BOOKED | FAILED
  orderNumber?: string | null  // Carrier pickup reference. Null when status is FAILED.
  shipmentId?: string | null  // Null for a standalone pickup not derived from an existing consignment.
  pickupDate?: string
  readyTime?: string
  closeTime?: string
  serviceCode?: string | null  // TNT only.
  collection?: BookingAddress
  items?: Items
  lastErrorCode?: string | null
  lastErrorMessage?: string | null
  source?: API | PORTAL
  createdByUserId?: string | null  // Null for API-created bookings.
  createdAt?: string
}

PickupBookingDetailResponse

{
  pickup: PickupBookingDetail
}

ShipmentSummary

{
  id: string  // Datashift shipment ID
  carrier: TNT_AU | BEX_AU
  connectorName: string
  orderNumber: string
  consignmentNumber?: string | null  // Null if not yet assigned by the carrier
  status: CONSIGNED | CANCELLED
  pickupStatus?: NOT_BOOKED | BOOKED | FAILED
  serviceCode: string
  source: API | PORTAL  // Whether the shipment was booked via the API or the portal UI
  fromSuburb?: string | null
  toSuburb?: string | null
  createdAt: string
}

ShipmentListResponse

{
  total: integer  // Total shipment count for this tenant (may exceed the current page)
  limit: integer  // Page size used for this response
  offset: integer  // Offset used for this response
  shipments: ShipmentSummary[]
}

ShipmentDetail

ShipmentSummary & {
  payer: Sender | Receiver | ThirdParty
  sender: BookingAddress
  collection: BookingAddress
  receiver: BookingAddress
  items: Items
  collectionDateTime: string
  collectionCloseTime: string
  customerReference?: string | null
  specialInstructions?: string | null
}

ShipmentDetailResponse

{
  shipment: ShipmentDetail
}

TrackingEvent

{
  timestamp: string
  status: string
  location?: string | null
  description?: string | null
}

TrackingInfo

{
  currentStatus: string
  eta?: string | null  // Estimated delivery date (YYYY-MM-DD)
  deliveredAt?: string | null  // Delivery timestamp, present only when delivered
  events: TrackingEvent[]  // Scan events in reverse-chronological order (most recent first)
}

TrackingResponse

{
  supported: boolean  // `false` when the matched carrier does not expose a tracking API
  tracking?: TrackingInfo  // Null if tracking is not yet available or not supported
}

PrintDevice

{
  id: string  // Use as `deviceId` in print requests
  label: string  // Human-readable name for the device (set during registration)
  printerName?: string | null  // OS printer name reported by the device client, if available
  lastUsedAt?: string | null  // When this device last received a print job
  createdAt: string
}

PrintDeviceListResponse

{
  devices: PrintDevice[]
}

PrintRequest

{
  deviceId: string  // ID of the print device to send the label to (from `GET /print-devices`)
}

PrintJobCreated

{
  jobId: string  // Datashift print job ID
}

LabelNotAvailableError

{
  message: string
  code: LABEL_NOT_AVAILABLE
}

PrintNotFoundError

{
  message: string
  code: LABEL_NOT_AVAILABLE | DEVICE_NOT_FOUND | SHIPMENT_NOT_FOUND
}

CarrierError

{
  message: string
  code?: string
}

Error

{
  message: string
  errors?: {
    message: string
    field?: string  // Field path for validation errors (e.g. `sender.postcode`)
    code?: string  // Carrier error code for carrier-returned errors
  }[]  // Validation errors use `{ field, message }`; carrier errors use `{ code, message }`
}