DataShift Logo

Schemas

ConnectorSummary

{
  id: string  // Use as `connectorId` in rate and booking requests
  carrier: TNT_AU | BEX_AU | TGE_AU
  name: string  // Human-readable label for this connector
  services: {
    serviceCode: string
    description: string
  }[]  // Available services for this connector. All three carriers (TNT AU, BEX AU, TGE AU) currently expose a static service catalog, so this is populated immediately — before any connection test — falling back to the static catalog until a successful test discovers account-specific active services instead. Only returns an empty array for a carrier with neither a static catalog nor a successful test on record.
}

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  // Contact email for this party. Used by all three carriers as the contact email on the relevant carrier document (e.g. TNT AU's address block, BEX AU's ContactEmail). TGE AU specifically requires an email on `collection` — falls back to the connector's configured fallback address if omitted, but booking fails if neither is present.
}

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 `timezone` if supplied, otherwise Australia/Sydney.
  localDateTime?: string  // Current local time (yyyy-MM-ddTHH:mm:ss, no TZ suffix). Used by TNT to determine booking cutoff eligibility. Defaults to the current time in `timezone` if supplied, otherwise Australia/Sydney.
  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)
  businessUnit?: IPEC | PRIORITY_AU  // Selects which TGE AU business unit config (account code / rate card) to use. Ignored by carriers other than TGE AU.
  timezone?: string  // IANA timezone of the collection location, used by TNT AU to default `date`/`localDateTime` when they are not supplied. Must be one of the seven distinct AU state/territory IANA zones (e.g. `Australia/Sydney`, `Australia/Perth`) — any other value is ignored and the server falls back to its own resolved timezone. Ignored by carriers other than TNT AU.
}

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)
  visibility?: shown | hidden  // "hidden" services are ones the tenant has chosen to de-prioritise in Settings — still returned, but callers may want to group/collapse them the way the portal does. Services switched fully "off" never appear here at all.
  rateId?: string  // Pass this back as `rateId` (and optionally the response's top-level `quoteId`) on `POST /{connectorId}/shipments` to link the booking to this specific rate. DataShift verifies it belongs to your tenant and matches the connector/serviceCode being booked, then populates the shipment's recorded price/ETA/transit from this rate rather than requiring you to resend them. Rejected if the rate's quote is more than 48 hours old. Optional — omit it and pass the price/eta/transitDays fields directly if you already track your own pricing.
}

AggregatedRateResult

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

AggregatedRateResponse

{
  quoteId: string  // Identifies this rate-shop call as a whole — every rate across every carrier in `results` was requested together under this id. Not the same as any individual rate's own `rateId`.
  results: AggregatedRateResult[]
}

SingleConnectorRateResponse

{
  quoteId: string  // Identifies this rate-shop call as a whole — every rate in `rates` was requested together under this id.
  rates: RatedService[]
}

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  // Packaging type: `CT` — Carton, `BX` — Box, `SA` — Satchel, `PA` — Pallet, `EN` — Envelope, `CR` — Crate, `DR` — Drum, `RO` — Roll, `BG` — Bag, `PC` — Piece. Sent to TNT AU as `PackagingCode`; TGE AU uses it to derive the shipment item description (it has no packaging-code field of its own).
  serviceCode: string  // Carrier service code from a prior `getRates` call
  collectionDateTime: string  // Pickup ready time, in wall-clock time local to the `collection` address (no TZ suffix — TNT AU's own rule is "in the Collection Location", never a fixed national zone). On TNT AU must be 07:00–17:00, ≥ 120 minutes before collectionCloseTime.
  collectionCloseTime: string  // Premises close time, local to the `collection` address, as an hhmm string. On TNT AU must be 13:00–19:00.
  payer: {
    type: Sender | Receiver | ThirdParty  // `ThirdParty` is rejected on TNT AU connectors — see `TNT_THIRD_PARTY_UNSUPPORTED` above.
    accountNumber?: string  // The account to bill. Required when `type` is `Receiver` or `ThirdParty` (see `PAYER_ACCOUNT_REQUIRED` above). Not required when `type` is `Sender` (the connector's own account is charged by default) — but on TNT AU, supplying one while `type` is `Sender` will still charge that account, so leave it blank unless that's intended.
    subAccount?: string  // TGE AU only (SubAccountCode) — disambiguates per-state accounts. Ignored by TNT AU and Border Express.
    thirdPartyAddress?: BookingAddress  // TNT AU only. The third-party account holder's address. Only meaningful when `type` is `ThirdParty` — but note TNT AU itself rejects `ThirdParty` bookings (see `TNT_THIRD_PARTY_UNSUPPORTED` above), so this is effectively unused for TNT AU today. BEX AU and TGE AU don't have a distinct third-party address concept and ignore it.
  }  // Who is billed for this shipment.

**Validation rules** (returned as 400 `errors[]` entries with these `code` values):
- `PAYER_ACCOUNT_REQUIRED` — `accountNumber` is required when `type` is `Receiver` or `ThirdParty` (all carriers).
- `TNT_THIRD_PARTY_UNSUPPORTED` — TNT AU has no third-party billing concept; `type: ThirdParty` is rejected on TNT AU connectors. Bill the receiver and supply their TNT account number instead.
  businessUnit?: IPEC | PRIORITY_AU  // Selects which TGE AU business unit config (account code / ShipmentID range) to use. Ignored by carriers other than TGE AU.
  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)
  rateId?: string  // Links this booking to one specific offered rate from a prior `getRates` call (that rate's `rateId` field — renamed from `quoteId` on the rate object; see the breaking-change note below). DataShift verifies it belongs to your tenant and matches the connector/serviceCode being booked, then uses its price/ETA/transit rather than the raw `quoted*` fields below. Rejected if the rate's quote is more than 48 hours old. Optional: omitting it (including a caller still sending the pre-rename `quoteId` field name, which is no longer read here) is treated exactly like never having rate-shopped through DataShift at all — the booking proceeds using the raw `quoted*` fields below, unverified. This is intentional, not an error: it's the same fallback bookings without any rate-shop call have always used. **Breaking change:** before this field existed, booking requests sent this same link as `quoteId`. That name now means something else entirely (see below) — a caller still sending the old `quoteId` field to link a rate will have it silently ignored rather than rejected, and should switch to `rateId`.
  quoteId?: string  // The rate-shop call `rateId` came from (the response's top-level `quoteId`) — a different value from the old, pre-rename meaning of `quoteId` on a booking request (which used to mean what `rateId` means now). Optional, and only checked when `rateId` is also supplied — if both are given and they don't actually belong together, the booking is rejected.
  quotedPriceExGstMinor?: integer  // Ex-GST price of the quote this was booked from, in minor currency units (cents). Only used when `rateId` is not supplied — informational, recorded as reported, not independently verified.
  quotedGstMinor?: integer  // GST component, in minor units. BEX AU only.
  quotedPriceIncGstMinor?: integer  // Inc-GST price, in minor units. BEX AU only.
  quotedCurrency?: string
  committedEta?: string  // The quote's `eta`, echoed back at booking time. Same format as `RatedService.eta`.
  quotedTransitDays?: integer  // The quote's `transitDays`, echoed back at booking time.
  bookingCutoff?: string  // The quote's `bookingCutoff`, echoed back at booking time.
  collectionCutoff?: string  // The quote's `collectionCutoff`, echoed back at booking time.
}

ConsignmentBookingRequest

BookingRequest & {
  packages: Package[]
}

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 | RECONCILE_NEEDED  // 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. `FAILED` means no pickup exists at the carrier — safe to retry. `RECONCILE_NEEDED` means the carrier accepted the pickup but DataShift failed to save the record — a real pickup exists, do NOT retry (that would book a second one); `labelWarning` carries the carrier's pickup reference to reconcile manually. This response's own `orderNumber` field above is NOT updated to reflect it, and `RECONCILE_NEEDED` is only ever reported here — `GET /shipments/{id}`'s `pickupStatus` has no record to compute it from and will read `NOT_BOOKED` instead, though its `orderNumber` may pick up the carrier reference later if a best-effort background sync succeeds.
  labelWarning?: string  // Present when the pickup booking failed or needs reconciliation (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  // Required for standalone (non-derived) pickups on TNT AU and TGE AU connectors — both reject a booking with no service code. Ignored by BEX AU. In derived mode (`shipmentId` given), the original shipment's service code is used automatically and this field is optional for every carrier.
  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 | TGE_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  // Required for standalone TNT AU and TGE AU pickups; ignored by BEX AU. Populated automatically in derived mode.
  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 | TGE_AU
  connectorName: string
  orderNumber: string
  consignmentNumber?: string | null  // Null if not yet assigned by the carrier
  status: CONSIGNED | CANCELLED
  pickupStatus?: NOT_BOOKED | BOOKED | FAILED  // In one rare case, a pickup the carrier actually accepted can show as `NOT_BOOKED` here (not `FAILED`) because DataShift failed to record the attempt at all — no pickup-booking row exists to report on. When this happens, `orderNumber` above may still reflect the carrier's pickup reference via a best-effort background sync, even though `pickupStatus` reads `NOT_BOOKED`.
  serviceCode: string
  serviceDescription?: string | null  // Human-readable service name as returned by the carrier at booking time (e.g. "Road Express")
  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
  payerAccountNumber?: string | null  // The account billed for this shipment, when it wasn't the connector's own.
  payerSubAccount?: string | null  // TGE SubAccountCode, when one was supplied.
  payerThirdPartyAddress?: BookingAddress  // TNT AU third-party billing address, when one was supplied.
  sender: BookingAddress
  collection: BookingAddress
  receiver: BookingAddress
  items: Items
  dangerousGoods: boolean
  carrierBookingRef?: string | null  // Datashift-generated booking reference handed to the carrier at booking time, for carriers whose own booking API doesn't return a usable reference of its own (currently only TGE AU). Null for other carriers.
  collectionDateTime: string
  collectionCloseTime: string
  customerReference?: string | null
  specialInstructions?: string | null
}

ShipmentDetailResponse

{
  shipment: ShipmentDetail
}

TrackingEvent

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

TrackingPod

{
  deliveryNotation?: string | null
  itemsReceived?: string | null
  signedBy?: string | null
  signatureImageUrl?: string | null  // Direct link to the signature image, always https
}

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) — guaranteed for every carrier.
  pod?: TrackingPod  // Proof of delivery, when the carrier supports it and it is available for this shipment. Not marked `nullable` deliberately: when unavailable this key is omitted from the response entirely (not present in the JSON) rather than sent as `null` — codegen that treats `nullable: false` + absent-key as "always present" will be wrong here, so check for the key's presence, not its value.
}

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 }`
}