List orders
GET /api/orders returns the orders you placed through the X3D website, up to 50 and newest first, authenticated by your signed-in session rather than an API key.
There is no API-key endpoint that lists orders. The submit call hands you one order code and that code is the handle you keep — store it the moment you get it. The one listing that exists, GET /api/orders, is authenticated by a signed-in browser session and returns the orders placed through the website checkout.
/api/ordersSigned inList the website orders belonging to the signed-in account.
Request#
No path parameters, no query parameters, no body. The account is taken from the session cookie, so there is nothing to pass and no way to ask for somebody else's orders. Call it from a signed-in page in the browser; an API key will not authenticate it.
const res = await fetch("/api/orders", { credentials: "include" });
if (res.status === 401) {
window.location.href = "/auth/login";
} else {
const { orders } = await res.json();
console.log(orders.length, orders[0]?.id, orders[0]?.status);
}Response#
{
"orders": [
{
"id": "CART-6f0f1c0e-2f2a-4d4b-9a1d-0b6f2f9c0a11",
"email": "[email protected]",
"type": "cart_order",
"status": "shipped",
"total": 24.87,
"items": "2x Hinge bracket — PLA, Black; 1x Cable clip — PETG, Cyan",
"shippingAddress": {
"name": "Ada Lovelace",
"line1": "1100 Congress Ave",
"city": "Austin",
"state": "TX",
"postal_code": "78701",
"country": "US"
},
"trackingNumber": "9400111899223197428490",
"carrier": "usps",
"createdAt": "2026-09-01T18:33:00.000Z",
"updatedAt": "2026-09-04T11:02:11.412Z"
}
]
}The documents are returned exactly as they are stored — nothing is projected away, and optional fields are absent rather than null when they were never set.
idstringrequired- Order id and document key. CART- for a cart checkout, ORD- for a single print order, STORE- for a store purchase, each followed by a UUID. This is not the X3D- code the print API returns.
emailstringrequired- The account the order belongs to, lowercased.
typestringrequired- Which flow created it. Store purchases are recorded as cart_order.
print_ordercart_order statusstringrequired- Fulfilment stage. Every order is created as placed. cancelled is terminal and off the pipeline, not a sixth stage. See /docs/studio/orders for what each one means.
placedprintedpackedshippeddeliveredcancelled totalnumberrequired- Amount charged in USD, including postage.
itemsstringoptional- One-line summary of what was ordered, e.g. "2x Hinge bracket — PLA, Black". With no manifest saved it falls back to a bare count for a cart ("2 items"), or the product title for a single store purchase. Truncated at 900 characters.
materialstringoptional- Single-print orders only. The material key, e.g. pla.
colorstringoptional- Single-print orders only. The colour as ordered.
qualitystringoptional- Single-print orders only: standard or premium.
dimensionsstringoptional- Single-print orders only. A human-readable size string.
quantitynumberoptional- Single-print orders only. Units ordered.
modelUrlstringoptional- gs:// URI of the model that was printed. Not fetchable directly by a browser.
shippingAddressobjectoptional- name, line1, line2 (optional), city, state, postal_code, country. Absent on orders where no address was collected.
trackingNumberstringoptional- Set by an operator when the parcel is handed over. Absent until then.
carrierstringoptional- Which carrier the tracking number belongs to. other means we have no tracking URL we trust.
upsfedexuspsother createdAtstringrequired- ISO 8601 timestamp. This is the sort key.
updatedAtstringrequired- ISO 8601 timestamp of the last status or tracking write.
Ordering and limits#
- At most 50 orders come back. The limit is fixed in the query — there is no cursor, offset or page parameter.
- They are sorted newest first by createdAt, in the application rather than in the database.
- The sort happens after the limit, so an account with more than 50 orders gets an arbitrary 50 of them sorted, not the newest 50. Keep your own record if you need complete history.
- Orders are matched on your email, lowercased and trimmed. There is no parameter that could reach another account's data.
- An account with no orders gets 200 and an empty array, as does a deployment with no order database configured.
| Status | Body | When |
|---|---|---|
| 200 | { "orders": [...] } | Signed in. The array may be empty. |
| 401 | { "error": "unauthorized" } | No session, or a session with no email. Note the lowercase word. |
Tracking orders you submitted with a key#
- 1Save the code on submit
The 201 from POST /api/print/orders carries code, statusUrl and apiStatusUrl. Write the code to your own database against your own order id before you do anything else.
- 2Poll each code you are still waiting on
GET /api/print/orders/{code} with the same key returns status, paid and tracking. Poll on a human timescale — prints take hours.
bashcurl https://x3dstudios.com/api/print/orders/X3D-K7M2QP \ -H "Authorization: Bearer $X3D_API_KEY" - 3Stop polling at a terminal status
DELIVERED, CANCELLED and REJECTED do not change again. Drop those codes from the poll loop.