API conventions

Behaviour shared by every endpoint, documented once rather than repeated on each page.

Base URL and versioning

https://developer.allthe.com/api/v1

The version is in the path. We will not make a breaking change inside v1 — new fields may appear on responses and new optional fields may be accepted on requests, so parse tolerantly: ignore fields you do not recognise rather than rejecting the payload. Anything that would break an existing integration ships as v2.

All requests and responses are JSON (Content-Type: application/json), except the OAuth token and revoke endpoints, which take form encoding as the spec requires.

Pagination

Collection endpoints are cursor-paginated and share one envelope:

{
  "data": [ ... ],
  "has_more": true,
  "next_cursor": "cmuk0y584000004l8x3h63vs6"
}

| Parameter | Description | | --- | --- | | limit | 1–100. Defaults to 25. Outside that range returns 422. | | cursor | Pass the previous response's next_cursor. |

Loop until has_more is false:

cursor=""
while :; do
  page=$(curl -s "$ALLTHE_API/products?limit=100${cursor:+&cursor=$cursor}" \
    -H "Authorization: Bearer $ALLTHE_TOKEN")
  echo "$page" | jq -c '.data[]'
  [ "$(echo "$page" | jq -r '.has_more')" = "true" ] || break
  cursor=$(echo "$page" | jq -r '.next_cursor')
done

Cursors are opaque record ids — do not construct or parse them. Do not use next_cursor as a "what changed since" marker: it points at a position in the current ordering, not a moment in time. For change notification, use webhooks.

Identifiers

| Kind | Looks like | Notes | | --- | --- | --- | | Resource id | cmuk0y584000004l8x3h63vs6 | Opaque, globally unique, stable. Compare for equality only. | | Order reference | SMP-ALGQS4ATP | Human-facing. Shown on paperwork and accepted wherever an order id is. | | Variant SKU | YP023BKBK | Identifies a colour/size on a base product. | | external_reference | yours | Your own order id, stored and echoed back. Not unique-checked by us. |

Order endpoints accept either the resource id or the reference in the path, so GET /orders/SMP-ALGQS4ATP and GET /orders/cmuk… both work.

Money

  • Every amount is a number in major units — 15.98 means £15.98. Never minor units, never a string.
  • currency is GBP throughout. There is no multi-currency support today.
  • Amounts are rounded to 2 decimal places at source, so what you see is what is charged.
  • Prices you are charged are net of VAT, with vat_amount reported separately. Do not infer VAT by subtraction; read the field.

Dates

All timestamps are ISO 8601 with a Z suffix, in UTC: 2026-09-27T21:52:40.361Z. There are no local-time or offset-bearing fields anywhere in the API.

Retries and duplicate orders

external_reference is your idempotency key. Send one on every POST /orders and a repeated call with the same value short-circuits: it returns the order that already exists rather than creating a second one.

This matters because order creation does real work — pricing, charging, artwork intake — and can legitimately take several seconds. Without an external_reference, a request that times out leaves you unable to tell whether the order was placed, and retrying can place it twice and charge you twice. With one, the retry is safe.

{ "external_reference": "your-system-order-4821", "items": [ ... ] }

Use an id from your own system that is stable across retries of the same logical order — not a random value generated per attempt, which defeats the point entirely.

Make it unique to you, not just unique within your system. external_reference is currently unique across the whole platform, not per account. If another customer has already used the value you send, you get 409 duplicate_external_reference rather than an idempotent hit — so a bare counter like 1001 can collide with somebody else's order.

Prefix it with something of your own: acme-1001, or a UUID. We deliberately return a generic error in that case and reveal nothing about the existing order, so a collision is indistinguishable from any other rejection — you cannot debug your way past it, only avoid it.

Also safe to repeat:

  • Reads (GET) and POST /orders/estimate have no side effects at all.
  • Cancelling twice returns 200 with already_cancelled: true, so a retry after a dropped response is not an error.

Errors

Every error shares one shape, and the HTTP status tells you whether retrying can help. See Errors & rate limits for the full table, the rate-limit headers, and back-off guidance.