Transport Haus is now part of the Empty Labs Network

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

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:

Node.js 18+
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.

Header
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

TopicRule
FormatJSON in and out. Send Content-Type: application/json on every POST.
MoneyAustralian dollars. price is ex GST, which is what we invoice. gst and priceIncGst are given alongside.
PricesA 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.
TimebilledMinutes is the time the price is charged on, in half hours.
DatesTimestamps are ISO 8601 in UTC. scheduledDate is YYYY-MM-DD and scheduledTime is HH:mm, Melbourne time.
IdsUUIDs. Keep the quote and booking ids you receive; they are how you look things up later.
Status codes2xx means it worked. Anything else carries an error object (see Errors).

Check your key

GET/v1/me

Returns the account a key belongs to. Handy for confirming a key works before you go live.

Response
{
  "account": { "id": "1d0c6f2e-...", "name": "Your Business" }
}

Create a quote

POST/v1/quotes

Prices 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

jobType

string

delivery (default) takes items from your warehouse to the address. maintenance is a callout at the address itself, such as a warranty repair.
address

stringrequired

Where the job is. A full street address with suburb, state and postcode, in Victoria.
floor

string

The floor at the address: ground (default), 1, 2 or 3+. Stairs take longer, so this moves the price.
lift

boolean

Whether there is a lift to that floor. Defaults to false.
pickupAddress

string

Delivery only. Where we collect from. Leave it out and we collect from the warehouse on your account in the delivery's state.
items

arrayrequired

One to 50 lines. See Describing items.
reference

string

Your own reference, such as a sales order number. Up to 80 characters. Carried onto the booking and shown on your invoice.
Request
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"
  }'
Response
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

GET/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

POST/v1/bookings

Books 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

quoteId

string

The quote to book. The job is booked exactly as quoted. A quote can be booked once, within 14 days.
address, items, ...

Instead of quoteId: every field from Create a quote. Priced and booked together.
recipient.name

stringrequired

Who is receiving the delivery, or who we meet on site.
recipient.phone

stringrequired

Their phone number, ideally an Australian mobile. With customer tracking on for your account, we text them on the day with an arrival time and a live tracking link, branded as your business.
reference

string

Your reference for the booking. Overrides the one on the quote.
notes

string

Anything the crew should know: access, parking, where the item goes. Up to 500 characters.

Book a quote

Request
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

Request
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" }
  }'
Response
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

GET/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

name

stringrequired

The product name, as on your website or invoice. Up to 120 characters.
quantity

integer

How many. 1 to 200, default 1.
size

string

S side table, chair, lamp. M armchair, coffee table, shelving. L three seater sofa, dining table, wardrobe. XL sectional sofa, king bed, modular shelving.
weightKg

number

The packed weight in kilograms. Used when no size is sent.
assembly

string

pre_assembled (default), assembly_required for us to assemble on site, or disassembly_reassembly for something that must come apart to get in.
fragile

boolean

Marble, glass, ceramics and the like. Handled with extra care, which takes longer.

Booking statuses

StatusMeaning
awaiting_dateBooked with us. We will confirm a date and time with you.
scheduledA date is confirmed. See scheduledDate and scheduledTime.
completedDelivered or attended.
cancelledCancelled. 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.

Response
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"
  }
}
StatusCodeWhen
400invalid_requestA field is missing or not valid. See field.
401unauthorizedNo key, a mistyped key, or a revoked one.
403trial_accountYour account is on a trial and cannot book yet. Quoting still works. Talk to us to finish setting up.
404not_foundNo such endpoint, or no quote or booking with that id on your account.
405method_not_allowedThe endpoint exists but not with that method.
409conflictThe same quote is being booked by another request at that moment. Retry shortly.
410quote_expiredThe quote is more than 14 days old. Request a new one.
422outside_service_areaThe address is outside Victoria.
422no_warehouseA delivery with no pickupAddress, on an account with no warehouse set up.
500internal_errorSomething 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.