Quickstart
This guide takes you from no account to a booked demo shipment with a PDF label
in about 10 minutes. Every call goes to https://api.transdirect.com.au with
your key in the api-key header. Demo bookings are free and are never sent to
a courier.
You need curl, and jq if you want to copy values out of responses
automatically. The SDK tabs use Node.js 18 or later.
-
Create a member account
Sign up at members.transdirect.com.au/sign-up. Signing up is free.
-
Get an API key
- Sign in at members.transdirect.com.au and go to Settings, then Integrations (direct link).
- Find Custom API and click Connect. If you already have a Custom API key, the button is Add another store.
- In the Connect Custom API dialog, enter the URL of the site or app that will use the key, tick the box to accept the Terms and Conditions and the Transit Warranty terms, then click Connect.
- Your key appears on the new row. Click the copy icon next to it.
Keep the key in an environment variable for the rest of this guide:
Terminal window export TD_API_KEY="paste-your-key-here"Authentication covers rotating and deactivating keys.
-
Switch on the demo courier
On the same row, open the ⋯ (More actions) menu and tick Demo courier, then accept the prompt. Within about a minute, quotes for this key include a courier called
demothat you can book for free. See testing with demo mode. -
Check the key works
Terminal window curl https://api.transdirect.com.au/api/v5/member \-H "api-key: $TD_API_KEY"A
200with your account details means the key works. A401with{"error":"missing_api_key"}or{"error":"invalid_api_key"}means the header is missing or the key is wrong. -
Get quotes
Send the pickup address, the delivery address and the items. Weights are in kilograms and dimensions in centimetres. Item
descriptionis the package type (Carton,Satchel,Palletand so on); what is inside goes ingoods_description. A booking with no goods description can be held for review after payment, so always send one.The name, email, phone and street address are optional for a quote, but a booking cannot be confirmed without them (and a business address also needs
company_name), so include them now.Terminal window curl https://api.transdirect.com.au/api/v5/quotes \-H "api-key: $TD_API_KEY" \-H "content-type: application/json" \-d '{"declared_value": 0,"goods_description": "Books","sender": {"name": "Alex Chen","company_name": "Example Pty Ltd","email": "dispatch@example.com","phone": "0291234567","address": "1 George Street","suburb": "SYDNEY","postcode": "2000","state": "NSW","country": "AU","type": "business"},"receiver": {"name": "Sam Taylor","email": "sam@example.com","phone": "0412345678","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 }]}' > quote.jsonexport BOOKING_ID=$(jq -r '.id' quote.json)jq '.quotes | keys' quote.jsonTerminal window npm install @transdirect/api-sdkimport { client, postApiV5Quotes } from '@transdirect/api-sdk';client.setConfig({ headers: { 'api-key': process.env.TD_API_KEY! } });const { data: quote, error } = await postApiV5Quotes({body: {declared_value: 0,goods_description: 'Books',sender: {name: 'Alex Chen', company_name: 'Example Pty Ltd',email: 'dispatch@example.com', phone: '0291234567',address: '1 George Street', suburb: 'SYDNEY', postcode: '2000',state: 'NSW', country: 'AU', type: 'business',},receiver: {name: 'Sam Taylor', email: 'sam@example.com', phone: '0412345678',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 }],},});if (error || !quote) throw new Error(JSON.stringify(error));const bookingId = String(quote.id);console.log(bookingId, Object.keys(quote.quotes));The response is
201. It is trimmed here, and your prices depend on your account’s rates:{"id": 34512345,"status": "new","status_code": "new","created_at": "2026-10-16T15:32:38+1100","quotes": {"aramex": {"total": 12.93,"price_insurance_ex": 12.93,"applied_gst": 1.18,"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" }},"demo": {"total": 42.9,"service": "demo","transit_time": "2-4 Business Days","rate_card_code": "DEMO","pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21"],"pickup_time": { "from": "12:00", "to": "17:00" }}},"quote_errors": [{ "courier": "allied_pallet", "code": "no_service", "serviceability": "route.no_residential", "retryable": false }]}idis the Transdirect booking id. Every quote creates a draft booking (statusnew); you use this id for every later call. A draft you never confirm is never charged.quotesis keyed by courier. The key (aramex,demo) is the value you send ascourierwhen you confirm. It is an object, so sort bytotalyourself.totalis in AUD and includes GST.quote_errorslists couriers that did not return a price, and why.
If there is no
demokey inquotes, check that Demo courier is ticked for this key (step 3), wait a minute and quote again. -
Confirm on the demo courier
Pick a pickup date from the demo quote’s
pickup_dates. Apickup_timeis a window writtenHH:MM-HH:MMthat must sit inside that courier’spickup_time; leave it out to use the courier’s default window (for today, the first window still open).Terminal window PICKUP_DATE=$(jq -r '.quotes.demo.pickup_dates[0]' quote.json)curl -i https://api.transdirect.com.au/api/v5/bookings/$BOOKING_ID/confirm \-H "api-key: $TD_API_KEY" \-H "content-type: application/json" \-d "{\"courier\": \"demo\",\"pickup_date\": \"$PICKUP_DATE\",\"pickup_time\": \"12:00-17:00\",\"payment_method\": \"demo\"}"import { postApiV5BookingsByIdConfirm } from '@transdirect/api-sdk';const { error: confirmError, response } = await postApiV5BookingsByIdConfirm({path: { id: bookingId },body: {courier: 'demo',pickup_date: quote.quotes.demo.pickup_dates[0],pickup_time: '12:00-17:00',payment_method: 'demo',},});if (confirmError) throw new Error(JSON.stringify(confirmError));console.log(response.status); // 204Success is
204 No Contentwith an empty body. The booking gets aDEMO-connote and moves toconfirmed. Nothing is charged and nothing is sent to a courier.A
400names what is wrong: for examplePickup dates invalid.when the date is not inpickup_dates, or the address fields that are missing. -
Download the label
Ask with
HEADwhether the label is ready (200ready,400not yet), then download it withGET. The demo label is watermarked DEMO.Terminal window curl -I https://api.transdirect.com.au/api/v5/bookings/$BOOKING_ID/label \-H "api-key: $TD_API_KEY"curl https://api.transdirect.com.au/api/v5/bookings/$BOOKING_ID/label \-H "api-key: $TD_API_KEY" \-o label.pdfimport { writeFile } from 'node:fs/promises';import { headApiV5BookingsByIdLabel, getApiV5BookingsByIdLabel } from '@transdirect/api-sdk';const head = await headApiV5BookingsByIdLabel({ path: { id: bookingId } });if (head.response.status === 200) {const { data: pdf } = await getApiV5BookingsByIdLabel({path: { id: bookingId },parseAs: 'arrayBuffer',});await writeFile('label.pdf', Buffer.from(pdf as ArrayBuffer));}If
GETreturns409with"error": "label_not_ready", wait a few seconds and try again. Use this endpoint rather than thelabelURL in the quote response. See get a label for A6 labels and other documents. -
Track it
Tracking takes the booking id, the connote or the courier’s reference:
Terminal window curl https://api.transdirect.com.au/api/v5/tracking/$BOOKING_IDimport { getApiV5TrackingByReference } from '@transdirect/api-sdk';const { data: tracking } = await getApiV5TrackingByReference({path: { reference: bookingId },});console.log(tracking?.state, tracking?.statusPhrase);The response gives an overall
state(such aspending,in_transitordelivered), a readableheadlineandsummary, and the events per item. A demo booking is never picked up, so it never gets courier scans. There are no webhooks: poll for updates. See tracking.
You can also check the booking itself at any time with
GET /api/v5/bookings/$BOOKING_ID. Read status_code (confirmed) in your
code; status is a display label such as “Confirmed”.
Going live
Section titled “Going live”A live booking is the same two calls on a real courier, with a real payment method. There is no separate live host or live key.
-
Add a card or PayPal account in the members area under Billing, then Payment Methods. Then list what your key can charge:
Terminal window curl https://api.transdirect.com.au/api/v5/members/payment-methods \-H "api-key: $TD_API_KEY"[{ "id": "nab_1234", "type": "credit_card", "masked_label": "VISA ending 444433......1111", "is_default": true }] -
Confirm with a real courier key from
quotesand thatidaspayment_method:{ "courier": "aramex", "pickup_date": "2026-10-19", "payment_method": "nab_1234" }
How payments work covers credit accounts, PayPal and payment errors.