Quote and create a booking
On the v5 API you create a booking by quoting it. Every quote request saves a draft booking and returns its id with the prices. You then confirm that draft with the courier you chose. There is no separate “create booking” call, and no single call that quotes and pays.
POST /api/v5/quotesRequires your API key in the api-key header. Quoting is free: a draft is never charged or sent to a courier until you confirm it.
This page walks through the whole sequence. For every quote field and option, see create a quote.
1. Quote
Section titled “1. Quote”Send the addresses, items and goods description. If you already have the sender and receiver contact details, include them now so the booking is ready to confirm. Otherwise you can add them later with update a booking.
curl -X POST https://api.transdirect.com.au/api/v5/quotes \ -H "api-key: $TD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "sender": { "name": "Sam Taylor", "company_name": "Example Books Pty Ltd", "email": "dispatch@example.com", "phone": "0291234567", "address": "1 Martin Place", "suburb": "SYDNEY", "state": "NSW", "postcode": "2000", "country": "AU", "type": "business" }, "receiver": { "name": "Jordan Lee", "email": "jordan@example.com", "phone": "0398765432", "address": "200 Collins Street", "suburb": "MELBOURNE", "state": "VIC", "postcode": "3000", "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" }'items[].descriptionis the package type (Carton,Satchel,Palletand so on). What is inside goes ingoods_description. See items and packaging.- Weights are in kg and dimensions in cm.
declared_valuesets the transit warranty cover.0means no cover.
Quote response
Section titled “Quote response”201 Created. Shortened here to two couriers; a real response lists every courier that quoted.
{ "id": 623630, "status": "new", "status_code": "new", "created_at": "2026-10-16T15:13:34+1100", "declared_value": 0, "description": "Books", "customer_reference": "ORDER-1042", "items": [ { "id": 46885332, "description": "Carton", "weight": 5, "length": 40, "width": 30, "height": 20, "quantity": 1, "sku": false } ], "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", "pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21"], "pickup_time": { "from": "09:00", "to": "16:00" } }, "couriers_please": { "total": 28.43, "price_insurance_ex": 28.43, "fee": 0, "applied_gst": 2.58, "insured_amount": 0, "service": "road", "transit_time": "1-2 Business Days", "pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21"], "pickup_time": { "from": "09:00", "to": "16:00" }, "pickup_ready_from_times": ["09:00"], "pickup_last_available_times": ["16:00"], "rate_card_code": "AVP10" } }, "quote_errors": [ { "courier": "allied_pallet", "code": "no_service", "serviceability": "route.no_residential", "retryable": false } ]}idis the booking id. Keep it: every later call uses it.quotesis keyed by courier. The key (for examplecouriers_please) is thecourieryou send to confirm. Sort bytotalyourself rather than relying on key order.quote_errorsexplains each courier that did not quote, for example a route it does not serve. A courier with"retryable": truemight quote if you try again.- The response also has a
labelURL on an older host. Ignore it and use get a label.
The SDK function is postApiV5Quotes. POST /api/v5/bookings also quotes and creates a draft, with the same request body.
2. Confirm
Section titled “2. Confirm”Choose a quote, then confirm with its key, one of its pickup_dates and a payment method. See confirm a booking for every field and error.
curl -X POST https://api.transdirect.com.au/api/v5/bookings/623630/confirm \ -H "api-key: $TD_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "courier": "couriers_please", "pickup_date": "2026-10-19", "payment_method": "nab_4821" }'204 No Content means the booking is paid and on its way to the courier. Take the payment_method id from list payment methods, or use credit for a credit account. While you build, confirm with "courier": "demo" and "payment_method": "demo" instead; see demo mode.
Confirm refuses a booking whose sender or receiver is missing a name, email, phone or address (or company_name for a business address). If you did not send them in step 1, update the booking first.
3. Follow it to the label and tracking
Section titled “3. Follow it to the label and tracking”-
Wait for
confirmed. Poll get a booking every minute or so and readstatus_code. When the courier accepts the job it isconfirmedandconnoteis set. A booking held for a check showspending_revieworon_holdfor a while; see bookings held for review. -
Download the label.
HEAD /api/v5/bookings/623630/labelreturns200when it is ready; thenGETit. See get a label. -
Track it. After pickup, poll
GET /api/v5/tracking/623630every hour or two untilstateisdelivered.
The whole flow in TypeScript
Section titled “The whole flow in TypeScript”import { client, postApiV5Quotes, postApiV5BookingsByIdConfirm, getApiV5BookingsById, headApiV5BookingsByIdLabel, getApiV5BookingsByIdLabel,} from '@transdirect/api-sdk';
client.setConfig({ headers: { 'api-key': process.env.TD_API_KEY! } });
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
// 1. Quote: creates draft booking with status_code "new"const { data: quote, error: quoteError } = await postApiV5Quotes({ body: { sender: { name: 'Sam Taylor', company_name: 'Example Books Pty Ltd', email: 'dispatch@example.com', phone: '0291234567', address: '1 Martin Place', suburb: 'SYDNEY', state: 'NSW', postcode: '2000', country: 'AU', type: 'business', }, receiver: { name: 'Jordan Lee', email: 'jordan@example.com', phone: '0398765432', address: '200 Collins Street', suburb: 'MELBOURNE', state: 'VIC', postcode: '3000', country: 'AU', type: 'residential', }, items: [{ description: 'Carton', weight: 5, length: 40, width: 30, height: 20, quantity: 1 }], goods_description: 'Books', declared_value: 0, },});if (quoteError || !quote) throw new Error(JSON.stringify(quoteError));
// 2. Pick the cheapest real courier (never the demo courier in production)const [courier, chosen] = Object.entries(quote.quotes) .filter(([key]) => key !== 'demo') .sort(([, a], [, b]) => a.total - b.total)[0];const bookingId = String(quote.id);
// 3. Confirm with a pickup date from that quote and a saved payment methodconst confirm = await postApiV5BookingsByIdConfirm({ path: { id: bookingId }, body: { courier, pickup_date: chosen.pickup_dates[0], payment_method: 'nab_4821' },});if (confirm.response.status !== 204) throw new Error(JSON.stringify(confirm.error));
// 4. Wait for the courier to accept, then download the labelfor (let attempt = 0; attempt < 30; attempt += 1) { const { data: booking } = await getApiV5BookingsById({ path: { id: bookingId } }); const ready = await headApiV5BookingsByIdLabel({ path: { id: bookingId } }); if (ready.response.status === 200) { const { data: pdf } = await getApiV5BookingsByIdLabel({ path: { id: bookingId } }); console.log('Label ready for', booking?.connote, pdf); break; } await sleep(60_000);}Errors from the quote request
Section titled “Errors from the quote request”| Status | Body | Cause |
|---|---|---|
400 | {"success":false,"error":{"name":"ZodError","message":"..."}} | sender or receiver is missing, or a field has the wrong type. |
400 | Malformed JSON in request body (plain text) | The body is not valid JSON. |
400 | {"errors":{"<field>":["..."]}} | A field failed a business rule. The key names the field, and items errors cover package types and sizes. |
401 | {"error":"missing_api_key"} or {"error":"invalid_api_key"} | No key, or a key that is not active. |
429 | {"error":"rate_limited"} | More than 600 requests a minute on this key. Wait for the retry-after seconds. |
500 | {"error":"booking_persist_failed"} | The quote was priced but the draft could not be saved. No booking id exists; retry the quote request. |
503 | {"error":"reference_data_unavailable","message":"Quote reference data is temporarily unavailable. Please try again."} | A temporary fault. Retry shortly. |
See create a quote for the full list.
Drafts you do not confirm
Section titled “Drafts you do not confirm”Drafts you never confirm are never charged and do not appear in list bookings. Leave them, or delete them.
Try it
Section titled “Try it”Open the API Explorer, add your key, and find Create a standard API quote. Every quote creates a draft booking, so use a demo-enabled key while you experiment.