For Developers
The Transport Haus API
Price a delivery and book it straight from your own systems. Your website, point of sale or warehouse software can quote a job in one call and book it in the next.
Overview
The API is for retailers with a Transport Haus account. It does two things for now: it quotes a delivery or maintenance job at your account's rates, and it books that job with us. Bookings arrive without a date. Our team confirms one with you, exactly like a Book later booking made in the portal, and the job then appears in your portal, and on your fortnightly invoice.
1. Quote
Send the address and the items. Get back a fixed price, the crew and the billed time.
2. Book
Send the quote id and who is receiving it. The booking is created straight away.
3. We schedule
We confirm a date and time with you. Check progress any time by booking id.
Base URL
https://mjrgbfylcvewfyufrfpa.supabase.co/functions/v1/api/v1
Quickstart
Sign in to the Client Portal, open API Keys and create a key. Put it in your server's environment as TRANSPORT_HAUS_API_KEY, then quote and book:
const API = "https://mjrgbfylcvewfyufrfpa.supabase.co/functions/v1/api/v1";
const headers = {
Authorization: `Bearer ${process.env.TRANSPORT_HAUS_API_KEY}`,
"Content-Type": "application/json",
};
// 1. Price the job
const { quote } = await fetch(`${API}/quotes`, {
method: "POST",
headers,
body: JSON.stringify({
address: "12 Smith St, Fitzroy VIC 3065",
floor: "2",
items: [{ name: "Three seater sofa", quantity: 1, size: "L" }],
reference: "SO-10482",
}),
}).then((r) => r.json());
console.log(quote.priceIncGst); // show your customer, or keep it
// 2. Book it. We confirm the date with you.
const { booking } = await fetch(`${API}/bookings`, {
method: "POST",
headers,
body: JSON.stringify({
quoteId: quote.id,
recipient: { name: "Sam Lee", phone: "0412 345 678" },
}),
}).then((r) => r.json());
console.log(booking.id, booking.status); // "awaiting_date"Authentication
Every request carries your key as a bearer token. Keys start with th_live_ and belong to your account: everything you quote and book with one is yours, and nothing in a request can name another account.
Authorization: Bearer th_live_...
We show a key once, when you create it, and keep only a fingerprint. Store it in a secret manager or environment variable and call the API from your server. Never put a key in a web page, a mobile app or a public repository, because anyone holding it can book work on your account. If a key leaks, revoke it under API Keys and it stops working immediately. You can hold up to ten active keys, so give each system its own and revoke them one at a time.
Conventions
| Topic | Rule |
|---|---|
| Format | JSON in and out. Send Content-Type: application/json on every POST. |
| Money | Australian dollars. price is ex GST, which is what we invoice. gst and priceIncGst are given alongside. |
| Prices | A quote is one fixed price, never a range. It is worked out from your account's rates, the drive, the floor and the items, and booking a quote books that exact price. |
| Time | billedMinutes is the time the price is charged on, in half hours. |
| Dates | Timestamps are ISO 8601 in UTC. scheduledDate is YYYY-MM-DD and scheduledTime is HH:mm, Melbourne time. |
| Ids | UUIDs. Keep the quote and booking ids you receive; they are how you look things up later. |
| Status codes | 2xx means it worked. Anything else carries an error object (see Errors). |
Check your key
/v1/meReturns the account a key belongs to. Handy for confirming a key works before you go live.
{
"account": { "id": "1d0c6f2e-...", "name": "Your Business" }
}Create a quote
/v1/quotesPrices one job and saves the quote for 14 days. Quoting is free and books nothing, so call it as often as you like, for example each time a shopper changes their cart. The same request always gives the same price.
Body
jobTypestring
delivery (default) takes items from your warehouse to the address. maintenance is a callout at the address itself, such as a warranty repair.addressstringrequired
floorstring
ground (default), 1, 2 or 3+. Stairs take longer, so this moves the price.liftboolean
false.pickupAddressstring
itemsarrayrequired
referencestring
curl https://mjrgbfylcvewfyufrfpa.supabase.co/functions/v1/api/v1/quotes \
-H "Authorization: Bearer $TRANSPORT_HAUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jobType": "delivery",
"address": "12 Smith St, Fitzroy VIC 3065",
"floor": "2",
"lift": false,
"items": [
{ "name": "Three seater sofa", "quantity": 1, "size": "L" },
{ "name": "Oak side table", "quantity": 2, "weightKg": 8, "assembly": "assembly_required" }
],
"reference": "SO-10482"
}'HTTP/1.1 201 Created
{
"quote": {
"id": "6f1c2a9e-4b7d-4c1e-9a53-0d2f8e7b1c44",
"object": "quote",
"jobType": "delivery",
"price": 240,
"gst": 24,
"priceIncGst": 264,
"currency": "AUD",
"billedMinutes": 120,
"crew": 2,
"pickupAddress": "4 Example Rd, Heidelberg West VIC 3081",
"address": "12 Smith St, Fitzroy VIC 3065",
"floor": "2",
"lift": false,
"items": [
{ "name": "Three seater sofa", "quantity": 1, "matchedCatalog": false },
{ "name": "Oak side table", "quantity": 2, "matchedCatalog": false }
],
"reference": "SO-10482",
"bookingId": null,
"expiresAt": "2026-11-05T03:12:44.000Z",
"createdAt": "2026-10-22T03:12:44.000Z"
}
}crew is one or two people, decided by what is being carried. matchedCatalog tells you whether we recognised an item from your product catalog. bookingId stays null until the quote is booked.
Retrieve a quote
/v1/quotes/{id}Returns a quote in the same shape as when it was created. Once it is booked, bookingId holds the booking it became.
Create a booking
/v1/bookingsBooks a job with us, without a date. There are two ways to call it: book a quote you already have, which guarantees the price you were given, or send the job itself and we quote and book it in one step.
Body
quoteIdstring
address, items, ...quoteId: every field from Create a quote. Priced and booked together.recipient.namestringrequired
recipient.phonestringrequired
referencestring
notesstring
Book a quote
curl https://mjrgbfylcvewfyufrfpa.supabase.co/functions/v1/api/v1/bookings \
-H "Authorization: Bearer $TRANSPORT_HAUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"quoteId": "6f1c2a9e-4b7d-4c1e-9a53-0d2f8e7b1c44",
"recipient": { "name": "Sam Lee", "phone": "0412 345 678" },
"notes": "Side gate is the easiest way in"
}'Quote and book in one call
curl https://mjrgbfylcvewfyufrfpa.supabase.co/functions/v1/api/v1/bookings \
-H "Authorization: Bearer $TRANSPORT_HAUS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-10482" \
-d '{
"address": "12 Smith St, Fitzroy VIC 3065",
"floor": "2",
"items": [{ "name": "Three seater sofa", "quantity": 1, "size": "L" }],
"reference": "SO-10482",
"recipient": { "name": "Sam Lee", "phone": "0412 345 678" }
}'HTTP/1.1 201 Created
{
"booking": {
"id": "b3e0d7a1-2c59-4f8e-8d61-7a9c0e4f2b13",
"object": "booking",
"status": "awaiting_date",
"jobType": "delivery",
"scheduledDate": null,
"scheduledTime": null,
"price": 240,
"gst": 24,
"priceIncGst": 264,
"currency": "AUD",
"billedMinutes": 120,
"crew": 2,
"pickupAddress": "4 Example Rd, Heidelberg West VIC 3081",
"address": "12 Smith St, Fitzroy VIC 3065",
"floor": "2",
"lift": false,
"items": [{ "name": "Three seater sofa", "quantity": 1 }],
"recipient": { "name": "Sam Lee", "phone": "0412 345 678" },
"reference": "SO-10482",
"quoteId": "6f1c2a9e-4b7d-4c1e-9a53-0d2f8e7b1c44",
"createdAt": "2026-10-22T03:15:02.000Z"
}
}Retries are safe
Booking the same quoteId twice returns the booking already made, with status 200 instead of 201, and never creates a second job. When you book without a quote, send an Idempotency-Key header that is unique to the order, such as your order number. A retry with the same key returns the same booking. If your request times out, retry it as it was.
Retrieve a booking
/v1/bookings/{id}Returns a booking in the shape above, with its current status. Once we have scheduled it, scheduledDate and scheduledTime are filled in. Any booking on your account can be read this way, including ones made in the portal.
Describing items
Each line of items describes one product. The better we know it, the closer the price. If the name matches a product in your catalog with us, we price it on that product's own handling figures and ignore size and weightKg. Otherwise we use the size you send, then the weight, and a medium piece if you send neither.
Item
namestringrequired
quantityinteger
sizestring
S side table, chair, lamp. M armchair, coffee table, shelving. L three seater sofa, dining table, wardrobe. XL sectional sofa, king bed, modular shelving.weightKgnumber
assemblystring
pre_assembled (default), assembly_required for us to assemble on site, or disassembly_reassembly for something that must come apart to get in.fragileboolean
Booking statuses
| Status | Meaning |
|---|---|
awaiting_date | Booked with us. We will confirm a date and time with you. |
scheduled | A date is confirmed. See scheduledDate and scheduledTime. |
completed | Delivered or attended. |
cancelled | Cancelled. Cancel a booking from the Client Portal, or ask us. |
Errors
A failed request returns an error object with a stable code to branch on, a sentence you can show a person, and, when one input is at fault, the field that caused it.
HTTP/1.1 400 Bad Request
{
"error": {
"code": "invalid_request",
"message": "items[0].quantity must be a whole number from 1 to 200",
"field": "items[0].quantity"
}
}| Status | Code | When |
|---|---|---|
| 400 | invalid_request | A field is missing or not valid. See field. |
| 401 | unauthorized | No key, a mistyped key, or a revoked one. |
| 403 | trial_account | Your account is on a trial and cannot book yet. Quoting still works. Talk to us to finish setting up. |
| 404 | not_found | No such endpoint, or no quote or booking with that id on your account. |
| 405 | method_not_allowed | The endpoint exists but not with that method. |
| 409 | conflict | The same quote is being booked by another request at that moment. Retry shortly. |
| 410 | quote_expired | The quote is more than 14 days old. Request a new one. |
| 422 | outside_service_area | The address is outside Victoria. |
| 422 | no_warehouse | A delivery with no pickupAddress, on an account with no warehouse set up. |
| 500 | internal_error | Something went wrong on our side. Retry with the same request. |
Limits
What the API does not do yet. Talk to us if you need any of these.
- Victoria only, for both the address and any pickup.
- One address per job. Multi-stop runs are booked in the portal.
- No dates or times. We confirm those with you after booking.
- No job extras, such as rearranging furniture. Add them in the notes and we will be in touch.
- No changes or cancellations through the API. Use the Client Portal.
- Keep to a sensible pace: a few requests a second is plenty for any checkout.
Building something with us?
Tell us what you are connecting and we will help you test it end to end before your first real booking.