Skip to content

How quoting works

A quote asks every courier available to your API key to price one shipment, and gives you back a draft booking with all the prices on it. You then confirm the draft with the courier you choose.

The one request you need is:

POST https://api.transdirect.com.au/api/v5/quotes

with your key in the api-key header. See Create a quote for the full reference.

Every quote request creates a booking with status new and returns its id. That id is the Transdirect booking id you use for every later call, such as confirm, update and labels. It is not your order number or a courier connote.

  • Each request makes a new draft, even for the same shipment. Nothing is served from a cache, so meta.cache.status is BYPASS.
  • A draft is never charged and never sent to a courier. Delete drafts you will not use with DELETE /api/v5/bookings/{id}, or leave them.
  • Drafts do not appear in List bookings, which skips status new. Keep the id from the quote response.
  • Quote requests take no courier, payment or pickup time. You choose those when you confirm, so a quote never books or pays for anything.

All four endpoints take the same request body and run the same pricing engine.

EndpointKeySuccessUse it for
POST /api/v5/quotesRequired201Integrations. This is the default.
POST /api/v5/bookingsOptional200The same as /quotes when you send a key. Without a key it quotes publicly listed couriers at public prices, limited to 10 requests an hour per IP address.
POST /api/v5/bookings/streamOptional200, Server-Sent EventsShowing prices as each courier answers. Same limits as POST /api/v5/bookings.
POST /api/v5/bookings/publicNone200Transdirect’s own public quote form. Publicly listed couriers only, no member pricing, limited to 30 requests a minute and 150 every 5 minutes per IP address. Use /api/v5/quotes in an integration.

Requests with a key share a limit of 600 a minute per key. Over a limit you get 429 with a retry-after header in seconds.

Call these endpoints from your server. Browsers on other websites cannot call the API directly, because it only allows Transdirect’s own sites as browser origins.

POST /api/v5/bookings/stream answers with Content-Type: text/event-stream. Each event has an event: name and a JSON data: line:

EventWhenData
readyFirstrequestId, carrierCount (couriers being asked), anonymous
bookingAs soon as the draft is savedbookingId, the draft booking id
quoteOnce per courier that priced the shipmentcarrier (the courier id) and quote, one entry shaped like a value in quotes. These prices are provisional.
carrier_errorOnce per courier that did notcourier, serviceability, retryable, reason
pingEvery 2 seconds while couriers are still answeringat, a timestamp. Ignore it.
completeLastresponse, the full quote response, the same body POST /api/v5/bookings returns
errorIf the stream failserror, message

Your key’s module settings, such as Quote display and surcharges, are applied after every courier has answered, so a quote event can show a price that changes or disappears in complete. Treat complete as the final list, and take the draft booking id from it or from the booking event.

The stream ignores customer_reference. If you do not need prices to appear one by one, use POST /api/v5/quotes.

A quote needs a sender, a receiver and at least one item. This body also carries the contact details you need to confirm it later:

{
"sender": {
"name": "Sam Taylor", "company_name": "Example Books Pty Ltd", "email": "dispatch@example.com", "phone": "0291234567",
"address": "1 Martin Place", "suburb": "SYDNEY", "postcode": "2000", "state": "NSW", "country": "AU", "type": "business"
},
"receiver": {
"name": "Jordan Lee", "email": "jordan@example.com", "phone": "0398765432",
"address": "200 Collins Street", "suburb": "MELBOURNE", "postcode": "3000", "state": "VIC", "country": "AU", "type": "residential"
},
"items": [
{ "description": "Carton", "weight": 5, "length": 40, "width": 30, "height": 20, "quantity": 1 }
],
"goods_description": "Books",
"declared_value": 0,
"customer_reference": "ORDER-1042"
}
  • Addresses. postcode, suburb, state, country and type (business or residential) are checked on both addresses. For Australian addresses the postcode must exist and the suburb must belong to it. Leave out state and it is filled in from the suburb and postcode. Look up valid combinations with Search locations.
  • Residential or business. type changes which couriers can quote and what they charge, so send what the address really is.
  • Items. description is the package type, such as Carton or Pallet, not what is inside. Weight is in kilograms and dimensions in centimetres. See Items and packaging.
  • Goods description. What is inside goes in the top-level goods_description (or description).
  • Contact and street details (name, company_name, email, phone, address, address2) are optional for a quote, but confirm needs name, email, phone and address on both addresses, plus company_name on a business address. Send them in the quote if you have them, or add them later with Update a booking.

Every field is listed in Create a quote.

Transit warranty: declared_value and include_warranty

Section titled “Transit warranty: declared_value and include_warranty”

Transit warranty is Transdirect’s cover for loss or damage in transit. Two request fields control it:

FieldDefaultEffect
declared_value0The value of the goods in AUD. It sets the warranty cover. 0, null or leaving it out means no cover.
include_warrantytrueSet false to quote without warranty. This also sets declared_value to 0 for the booking.

include_warranty defaults to true, so a non-zero declared_value on its own adds the warranty fee to every price.

If your key’s module settings have Include warranty on all requests via this module switched on, a request whose declared_value is missing or 0 is quoted with the module’s Transit warranty value instead. So the only sure way to quote without warranty is "include_warranty": false.

In each quote, price_insurance_ex is the price without warranty, fee is the warranty fee and insured_amount is the cover you get. Cover is capped per courier, so insured_amount can be lower than declared_value, and some couriers include cover with no fee. A booking with a declared value over $20,000 is held for a manual check after payment. Read the terms on the transit warranty page.

The response is the draft booking. The prices are in quotes, an object keyed by courier id:

{
"id": 623630,
"status": "new",
"status_code": "new",
"quotes": {
"aramex": {
"total": 12.93,
"price_insurance_ex": 12.93,
"fee": 0,
"applied_gst": 1.18,
"insured_amount": 0,
"service": "road",
"transit_time": "2 Business Days",
"transit": { "text": "2 Business Days", "type": "business_days", "min_days": 2, "max_days": 2 },
"pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21"],
"pickup_time": { "from": "09:00", "to": "16:00" },
"rate_card_code": "",
"tiers": [
{
"identifier": 2,
"description": "Multipickup 3 Price (Minimum 3 consignments per collection)",
"total": 10.18,
"price_insurance_ex": 10.18,
"fee": 0,
"applied_gst": 0.93,
"insured_amount": 0
}
]
},
"couriers_please": {
"total": 28.43,
"service": "road",
"transit_time": "1-2 Business Days"
}
},
"quote_errors": [
{ "courier": "allied_pallet", "code": "no_service", "serviceability": "route.no_residential", "retryable": false }
]
}

This is shortened. Create a quote has a full response.

FieldMeaning
totalWhat you pay, in AUD, including GST and any transit warranty fee.
price_insurance_exThe price without the transit warranty fee.
feeThe transit warranty fee. 0 when there is no cover.
applied_gstThe GST included in the price before the warranty fee, price_insurance_ex.
insured_amountThe transit warranty cover for this courier, in AUD.
tiersFrequent-shipper prices, when your account can use them. See Frequent rates and tiers.

The key of each entry in quotes (aramex, couriers_please, tnt_road_express and so on) is the value you send as courier when you confirm. Use the key from the quote response, not a courier name.

The entries come back cheapest first, but quotes is a JSON object and many JSON libraries do not keep key order. Sort by total yourself before you show a list or pick the cheapest.

If your key’s Quote display setting is Display cheapest or Display cheapest and fastest, the response holds only that one or two quotes.

FieldMeaning
serviceThe courier’s service, for example road.
transit_timeEstimated delivery time as text, for example "1-2 Business Days".
transitThe same estimate as data: type (business_days, hours, same_day, overnight, date_time or unknown) with min_days and max_days, or min_hours and max_hours.
pickup_datesThe pickup dates you can choose at confirm, as YYYY-MM-DD. Today is left out once the courier’s same-day cutoff has passed.
pickup_timeThe courier’s standard pickup window, {"from": "HH:MM", "to": "HH:MM"}.
pickup_windowThe rules behind the window: operatingStart, operatingEnd, sameDayCutoff, minWindowMinutes, incrementMinutes and fixed. When fixed is true (for example Aramex and Couriers Please) the courier only takes its standard window.
pickup_ready_from_times, pickup_last_available_timesFor couriers with a flexible window, the “ready from” and “last available” times you can offer, as HH:MM.
pickupAvailabilityThe same dates and windows in the shape a date and time picker needs.
tail_liftPresent only when you asked for a tail lift and this courier cannot provide one for your items: {"available": false, "reason": "weight", "message": "..."}. The price stands, but confirming this courier with tail lift fails.

To check pickup options again before you confirm, call Pickup availability.

  • id, status and status_code: the draft booking. Both statuses are "new". If the draft cannot be saved, an authenticated quote returns 500 booking_persist_failed instead of 201 with id: null. Retry the quote request before you try to confirm.
  • sender, receiver, items: what was saved on the draft, with their ids.
  • customer_reference: present when you sent one.
  • label: ignore it. Fetch labels with GET /api/v5/bookings/{id}/label once the booking is confirmed.
  • service_notices: optional notices that a courier is temporarily unavailable, as {courier, kind, message}. Show the message and do not offer that courier.
  • meta: diagnostics such as timings. Do not build logic on it.

Not every courier answers: check quote_errors

Section titled “Not every courier answers: check quote_errors”

A quote response shows the couriers that answered. It does not promise that every courier was asked and answered. Alongside quotes, every response has a quote_errors array naming each courier that did not produce a price:

{
"quote_errors": [
{ "courier": "northline", "code": "no_service", "serviceability": "route.not_serviced", "retryable": false },
{ "courier": "aramex", "code": "no_response", "serviceability": "carrier.unavailable", "retryable": true }
]
}
FieldMeaning
courierThe courier id that did not quote.
codeA broad category: no_service, no_response, invalid_response or courier_error.
serviceabilityThe reason. See the table below.
retryabletrue when the problem is temporary and quoting again may succeed.
serviceabilityMeaningA price is missing?
route.not_servicedThe courier does not cover this lane, area or zone.No
route.restricted_postcodeThe pickup or delivery postcode is outside the courier’s coverage.No
route.no_residentialThe courier does not deliver to residential addresses on this lane.No
items.not_servicedThe courier does not take this package type or packaging.No
items.over_limitAn item is over the courier’s weight or size limits.No
service.unavailableThe courier has no service or rate for this shipment.No
carrier.unavailableThe courier’s system timed out or could not be reached.Yes. Quote again.
config.errorA credentials or configuration problem with the courier.Yes. Quoting again will not help.
courier.errorThe courier sent a response that could not be read, or another failure.Yes

An empty quotes object means no courier priced the shipment. You still get a draft booking id, and quote_errors says why each courier did not quote:

  • All genuine “does not carry this” reasons: change the shipment. Common fixes are a different package type (heavy cartons often need to be a pallet, skid or crate), smaller or lighter items, or a correct suburb and postcode.
  • Any missing-price reason, such as carrier.unavailable: quote again shortly.

If a whole request fails before any courier is asked, you get an HTTP error instead, for example 400 for an invalid suburb. See Create a quote.

Couriers Please and Aramex can return a frequent-shipper price in tiers. It is for accounts that send at least 3 consignments per collection. Send its identifier as tier when you confirm. See Frequent rates.

Why your prices can differ from the website

Section titled “Why your prices can differ from the website”

A quote made with your API key is priced for your account:

  • Your courier discounts. Discounts on your account apply to that courier’s price.
  • Your couriers. Couriers switched off for your account are not quoted.
  • Your key’s module settings. Surcharges, a fixed price when quoting fails, quote display and default warranty, set on the key’s page under Settings, then Integrations in the members area. See API key configuration.
  • Your request. Residential or business addresses, tail lift, declared value and pickup date all change prices.

A quote on the public website, or one sent without a key, uses public prices and only the couriers listed publicly.

In demo mode, quotes also include a demo courier with a test price, which you can confirm without paying or sending anything.