Create a quote
Price a shipment with every courier available to your key. The response is a draft booking with one quote per courier.
POST /api/v5/quotesRequires your API key in the api-key header. Send the body as JSON with Content-Type: application/json.
Each call creates a new draft booking with status new. Nothing is charged or sent to a courier until you confirm it. Read How quoting works for the concepts behind this page.
Request body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
sender | object | Yes | Pickup address. See Address fields. |
receiver | object | Yes | Delivery address. See Address fields. |
items | array | Yes | At least one item. See Item fields. |
goods_description | string | No | What is inside the parcels, for example "Books". description is accepted as the same field. |
declared_value | number | No | Value of the goods in AUD, which sets the transit warranty cover. Defaults to 0, no cover. Must not be negative. |
include_warranty | boolean | No | Defaults to true, so a non-zero declared_value adds the warranty fee. false quotes without warranty and sets declared_value to 0. See transit warranty. |
tailgate_pickup | boolean | No | Ask for a tail lift truck at pickup. Only applies when an item is over 25 kg. |
tailgate_delivery | boolean | No | Ask for a tail lift truck at delivery. Only applies when an item is over 25 kg. |
pickup_date | string | No | Preferred pickup date, YYYY-MM-DD. Must not be before today in Sydney. |
instructions | object | No | {"pickup": "...", "delivery": "..."}, free-text notes for the drivers. Any delivery text is saved as authority to leave at the front door followed by your text, for example "Authority to leave at front door;Leave at reception". |
customer_reference | string | No | Your own reference, such as an order number. Up to 80 characters, trimmed. Saved on the booking and returned in the response. |
referrer, requesting_site | string | No | Free text saved on the booking to record where the request came from, for example your app name and your site address. |
Address fields
Section titled “Address fields”| Field | Type | Required | Description |
|---|---|---|---|
postcode | string | Yes | For Australia, a 4-digit postcode that exists. Send it as a string to keep leading zeros ("0800"). |
suburb | string | Yes | Must belong to the postcode. Use Search locations to find valid pairs. |
state | string | Yes, unless it can be filled in | For Australia one of NSW, VIC, QLD, WA, SA, TAS, ACT, NT. Filled in from the suburb and postcode if you leave it out. |
country | string | Yes | Two-letter country code, for example AU. |
type | string | Yes | business or residential. |
name | string | To confirm | Contact name, up to 255 characters. |
company_name | string | To confirm a business address | Up to 255 characters. |
address | string | To confirm | Street address, up to 255 characters. |
address2 | string | No | Second street address line, up to 255 characters. |
email | string | To confirm | Must be a valid email address when not empty. |
phone | string | To confirm | Up to 50 characters. |
Fields marked “To confirm” are not needed for a quote, but confirm fails without them. Send them in the quote, or add them later with Update a booking.
Item fields
Section titled “Item fields”| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | The package type, for example Carton, Satchel or Pallet. See the package types. |
weight | number | Recommended | Weight of one package in kilograms. Up to 2000. |
length, width, height | number | Recommended | Dimensions of one package in centimetres. Up to 500 each. |
quantity | integer | Yes | Number of identical packages. Up to 999. |
sku | string | No | Your product SKU. |
If you leave out a weight or dimension, your key’s default package size is used, so send real measurements. See Items and packaging.
Example request
Section titled “Example request”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", "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" }'With the TypeScript SDK, using the same body:
import { client, postApiV5Quotes } from '@transdirect/api-sdk';
client.setConfig({ headers: { 'api-key': process.env.TD_API_KEY! } });
const { data: quote, error } = 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', 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', },});if (error) throw new Error(JSON.stringify(error));
// quotes is an object keyed by courier id. Sort it yourself.const cheapest = Object.entries(quote.quotes).sort(([, a], [, b]) => a.total - b.total)[0];console.log(`Booking ${quote.id}: ${cheapest?.[0]} at $${cheapest?.[1].total}`);Response
Section titled “Response”201 Created. The draft booking, with a quote from each courier that priced the shipment. This example is shortened to two quotes and two quote errors; a real response usually has more of each.
{ "id": 623630, "booked_at": "2026-10-16T15:13:34+1100", "booked_by": "sender", "created_at": "2026-10-16T15:13:34+1100", "updated_at": "2026-10-16T15:13:34+1100", "status": "new", "status_code": "new", "declared_value": 0, "insured_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 } ], "sender": { "id": 69194889, "name": "Sam Taylor", "company_name": "Example Books Pty Ltd", "email": "dispatch@example.com", "phone": "0291234567", "address": "1 Martin Place", "address2": "", "suburb": "SYDNEY", "postcode": "2000", "state": "NSW", "country": "AU", "type": "business" }, "receiver": { "id": 69194890, "name": "Jordan Lee", "company_name": "", "email": "jordan@example.com", "phone": "0398765432", "address": "200 Collins Street", "address2": "", "suburb": "MELBOURNE", "postcode": "3000", "state": "VIC", "country": "AU", "type": "residential" }, "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", "2026-10-22", "2026-10-23", "2026-10-26", "2026-10-27", "2026-10-28", "2026-10-29", "2026-10-30"], "pickup_time": { "from": "09:00", "to": "16:00" }, "pickupAvailability": { "dates": ["2026-10-19", "2026-10-20", "2026-10-21", "2026-10-22", "2026-10-23", "2026-10-26", "2026-10-27", "2026-10-28", "2026-10-29", "2026-10-30"], "dateLabels": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "times": [{ "from": "09:00", "to": "16:00" }], "earliest": { "day": "Monday", "date": "2026-10-19", "time": { "from": "09:00", "to": "16:00" }, "times": [{ "from": "09:00", "to": "16:00" }], "readyFromTimes": ["09:00"], "lastAvailableTimes": ["16:00"] }, "pickupWindow": { "operatingStart": "09:00", "operatingEnd": "16:00", "displayStart": "09:00", "displayEnd": "16:00", "sameDayCutoff": "14:00", "minWindowMinutes": 0, "incrementMinutes": 30, "fixed": true, "currentTimeInPickupTz": "15:13", "senderUtcOffsetMinutes": 660 }, "readyFromTimes": ["09:00"], "lastAvailableTimes": ["16:00"], "todayReadyFromTimes": [], "fixedPickupTime": true }, "pickup_window": { "operatingStart": "09:00", "operatingEnd": "16:00", "displayStart": "09:00", "displayEnd": "16:00", "sameDayCutoff": "14:00", "minWindowMinutes": 0, "incrementMinutes": 30, "fixed": true, "currentTimeInPickupTz": "15:13", "senderUtcOffsetMinutes": 660 }, "pickup_ready_from_times": ["09:00"], "pickup_last_available_times": ["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 } ] }, "tnt_road_express": { "total": 51.58, "price_insurance_ex": 51.58, "fee": 0, "applied_gst": 4.69, "insured_amount": 0, "service": "road", "transit_time": "1 Business Day", "transit": { "text": "1 Business Day", "type": "business_days", "min_days": 1, "max_days": 1 }, "pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21", "2026-10-22", "2026-10-23", "2026-10-26", "2026-10-27", "2026-10-28", "2026-10-29", "2026-10-30"], "pickup_time": { "from": "07:00", "to": "19:00" }, "pickup_cutoff": "15:00", "pickup_window": { "operatingStart": "07:00", "operatingEnd": "19:00", "displayStart": "07:00", "displayEnd": "19:00", "sameDayCutoff": "16:00", "minWindowMinutes": 120, "incrementMinutes": 30, "fixed": false, "currentTimeInPickupTz": "15:13", "senderUtcOffsetMinutes": 660 }, "pickup_ready_from_times": ["07:00", "07:30", "08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30"], "pickup_last_available_times": ["09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "12:00", "12:30", "13:00", "13:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30", "18:00", "18:30", "19:00"], "rate_card_code": "76" } }, "quote_errors": [ { "courier": "allied_pallet", "code": "no_service", "serviceability": "route.no_residential", "retryable": false }, { "courier": "team_global_express", "code": "no_service", "serviceability": "service.unavailable", "retryable": false } ], "label": "https://www.transdirect.com.au/api/bookings/623630/label", "notifications": { "email": false, "sms": false }, "pickup_window": ["2026-10-16T09:00:00+1100", "2026-10-16T17:00:00+1100"], "connote": null, "charged_weight": 0, "scanned_weight": 0, "special_instructions": "", "pickup_instructions": null, "tailgate_pickup": false, "tailgate_delivery": false, "meta": { "api_version": "v5", "cache": { "status": "BYPASS" }, "timing": { "duration_ms": 2371, "boxing_duration_ms": 0 }, "anonymous": false, "quote_count": 2 }}TNT’s quote in a real response also carries a pickupAvailability object, left out here for length. This quote was made at 15:13 on a Friday. Aramex’s same-day cutoff is 14:00, and this TNT depot’s cutoff is 15:00 (pickup_cutoff), so both start on Monday. pickup_cutoff is the per-quote collection cutoff where a courier gives one, and it can be earlier than pickup_window.sameDayCutoff.
Response fields
Section titled “Response fields”| Field | Description |
|---|---|
id | The draft booking id. Use it to confirm, update or delete the booking. A failed draft save returns 500 booking_persist_failed instead of a successful response with id: null. |
status, status_code | Both "new" on a fresh quote. |
quotes | One entry per courier that priced the shipment, keyed by the courier id you send as courier when you confirm. Sort by total yourself. Field meanings are in How quoting works. |
quote_errors | One entry per courier that did not price it. Check this before you call a price the cheapest. See quote errors. |
service_notices | Optional. Couriers that are temporarily unavailable, as {courier, kind, message}. |
customer_reference | The reference you sent, or null if it could not be saved. Present only when you sent one. Set it again with Update a booking if it is null. |
sender, receiver, items | What was saved on the draft. |
description, declared_value | The goods description and declared value saved on the draft. |
special_instructions, pickup_instructions | The delivery and pickup instructions saved on the draft. |
label | Do not use. Fetch labels with GET /api/v5/bookings/{id}/label after you confirm. |
pickup_window, connote, charged_weight, scanned_weight | Placeholders on a draft. Read them from Get a booking once the booking is confirmed. |
meta | Diagnostics, such as timing. Do not build logic on it. |
Errors
Section titled “Errors”| Status | Body | Cause |
|---|---|---|
400 | {"success":false,"error":{"name":"ZodError","message":"..."}} | The body does not match the schema, for example sender is not an object, pickup_date is in the past or customer_reference is over 80 characters. message is a JSON string listing each problem. |
400 | {"error":{"name":"ZodError","issues":[...]},"requestId":"..."} | The same kind of problem when the request is sent without Content-Type: application/json. |
400 | {"errors":{"receiver":{"suburb":["Invalid Suburb"]}}} | An address failed a check: a missing field, a postcode that does not exist, a suburb that is not in the postcode, or a bad type, country or email. May also include substituted_fields when a value came from your key’s settings rather than your request. |
400 | {"errors":{"items":[{"weight":["Weight must be 2000 kg or less."]}]}} | An item is missing a value, is over a limit, or has an unknown package type. A request with no items returns {"errors":{"items":["Required"]}}. |
400 | {"status":"error","message":"Please submit separate orders as these products ship from different Warehouses"} | Your key uses multiple warehouses and the items’ SKUs belong to more than one warehouse. Quote each warehouse’s items separately. |
401 | {"error":"missing_api_key"} or {"error":"invalid_api_key"} | No key, or a key that is not active. |
404 | {"error":"unknown_member","member_id":123} | The account behind the key could not be loaded. |
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 problem on Transdirect’s side. Try again. |
A courier that cannot quote is not an error: the request still returns 201, and the courier appears in quote_errors.
Try it
Section titled “Try it”Open the API Explorer, paste your API key into the authentication box, and find Create a standard API quote. Each request creates a real draft booking on your account. Use demo mode to test confirming without charges.