Skip to main content
These hold on every route.

The envelope

An answer is always four fields. A refusal has the same four as a success.
Some answers carry more beside those four. A list has pages and total. Anything else a route answers that is not the thing itself is under meta.

Lists

data is the rows. A paged list says how many pages and rows there are in all, beside it.
A few lists are short by nature and are not paged: a store holds 250 products at most, so the list of products is all of them.

IDs

UUIDs, as text: 8d0a0f6e-3f0b-4c57-9a55-0c1f3f5d7a10. Do not read meaning into one or make one up. The ID in a checkout’s address is its order’s ID. An order’s ID is the exception. One made since October 22, 2026 reads a2855h0p1y4-71329ed4d95-1000427: random characters, and a number at the end that counts every order on Shoply. An order made before keeps its UUID. Treat either as text of up to 36 characters, and do not check its shape.

Dates

ISO 8601 in UTC: 2026-10-11T08:15:44Z. Where a route takes a day (from, to, an expiry), it is YYYY-MM-DD, and tz says which time zone that day is in, as an IANA name such as Europe/Berlin. Left out, UTC.

Money

Plain decimal numbers in the store’s own currency: 9.99 is nine ninety-nine. The store’s currencyCode says which currency. An order also says the currency its buyer was charged in, when that differs.

Saving

A PUT saves a thing whole, as the dashboard’s forms do. Read it, change what you want changed, and send all of it back. A field left out goes back to its default, unless its description says it stays as it is. Where only one thing about something changes often, it has a route of its own that takes just that: a product’s visibility, whether a sale is on.

Not overwriting somebody else’s change

Things you save whole carry updatedAt. Send it back as expectedUpdatedAt and the save is refused with stale_write if the thing has changed since you read it, with currentUpdatedAt beside the refusal. Leave it out and your save is written whatever happened in between.

Sending a body

JSON, with Content-Type: application/json, up to 2 MB. A picture is the exception: it is sent as multipart/form-data in a field its route names. Large files (a variant’s downloads, an expense’s receipt) are not sent to the API at all: the API answers a signed address, and you send the file there.

Every answer names its request

X-Request-Id is the ID of that request in the store’s record of API requests, which keeps what was sent and what came back for 30 days. Quote it when you ask for help.