X3DStudios

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.

Two separate order records
Orders submitted with an API key are not in this list and never will be. They are stored separately, use uppercase statuses like RECEIVED and PRINTING, and are read one at a time through GET /api/print/orders/{code}.
GET/api/ordersSigned in

List 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.

From a signed-in page
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#

200
{
  "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.

Order object
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.
StatusBodyWhen
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#

  1. 1
    Save 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.

  2. 2
    Poll 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.

    bash
    curl https://x3dstudios.com/api/print/orders/X3D-K7M2QP \
      -H "Authorization: Bearer $X3D_API_KEY"
  3. 3
    Stop polling at a terminal status

    DELIVERED, CANCELLED and REJECTED do not change again. Drop those codes from the poll loop.

If you lose a code
There is no lookup by email, address or file name. The code is in the confirmation email sent once the order is paid, and it is in the URL of that order's status page. If you have neither, ask us at /contact.