X3DStudios

List orders

GET /api/orders returns every order on your X3D account, website and print API alike, newest first, authenticated by your signed-in session rather than an API key.

There is no REST endpoint that lists orders with an API key. The submit call hands you one order code and that code is the handle you keep — store it the moment you get it. The listing that exists, GET /api/orders, is authenticated by a signed-in browser session and returns every order on the account: website orders and orders placed with the account's API key. It is what /orders and its statement are built from. With a key and no browser, the MCP tool list_my_orders gives the same view, capped at the 20 most recent.

Two order stores, one list
Website orders and print API orders are stored separately, and this endpoint reads both. An API order comes back with its X3D- code as id, type print_order, source api, and its uppercase status mapped onto the stages below: RECEIVED (and an unpaid PENDING_PAYMENT) as placed, ACCEPTED and PRINTING as queued, REJECTED as cancelled. The exact status is on 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": "2610014",
      "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. A print API order's id is its X3D- code. New website orders are numbered by month: two digits of year, two of month, then a count within that month, so 2610014 is the 14th order paid in October 2026 (Central time). A number is given only when an order is paid, so the numbers run without gaps from 2610019 on; before 13 October 2026 a number was taken when the checkout opened, which left gaps where a checkout was never paid. The first numbered orders, 10001 to 10005, keep their numbers. Orders placed before numbering keep their original id: CART- for a cart checkout, ORD- for a single print order, STORE- for a store purchase, each followed by a UUID.
emailstringrequired
The order's email, lowercased. For a print API order it is the order's main contact, which may not be the account's own address.
typestringrequired
Which flow created it. Store purchases are recorded as cart_order.print_ordercart_order
statusstringrequired
Fulfilment stage. Every order is created as placed. An order the customer collects goes placed, queued, printed, packed, picked_up, with no shipped or delivered. cancelled is terminal and off the pipeline, not a stage of its own. A print API order never reaches packed or picked_up. See /docs/studio/orders for what each one means.placedqueuedprintedpackedshippeddeliveredpicked_upcancelled
sourcestringoptional
Where the order came from. api on every print API order; absent on website orders placed before it was recorded.
fulfilmentstringoptional
Present on orders entered by hand: pickup when the customer collects it. Absent means posted.shippickup
totalnumberrequired
Amount charged in USD, including postage. A print API order reports its estimatedCost, or 0 before it was priced.
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. On a print API order it is the file name.
materialstringoptional
Single-print orders only, print API orders included. 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 website orders and 100 print API orders come back. The limits are fixed in the queries — there is no cursor, offset or page parameter.
  • The two lists are merged and sorted newest first by createdAt, in the application rather than in the database.
  • The 100 API orders are the newest 100. The website sort happens after its limit, so an account with more than 50 website orders gets an arbitrary 50 of them sorted, not the newest 50. Keep your own record if you need complete history.
  • Website orders are matched on the order's email, and print API orders on the account that placed them, both 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 address or file name. The code is in the confirmation email sent once the order is paid, in the URL of that order's status page, on /orders when you are signed in as the account that placed it, and in the MCP tool list_my_orders. If none of those has it, ask us at /contact.

Questions

Is there an API-key endpoint that lists my orders?

Not on the REST API, which has exactly two order endpoints: POST /api/print/orders to submit, and GET /api/print/orders/{code} to read one order back. The MCP server's list_my_orders tool does take the key: it returns the account's 20 most recent orders from every store, print API orders included. See /docs/api/mcp.

Do orders I submitted with my API key appear in GET /api/orders?

Yes, since 13 September 2026. The response reads both order stores, so an X3D- order placed with the account's key sits in the same list as its website orders, mapped onto the same lowercase stages. Read it in full, with the exact status, through GET /api/print/orders/{code}.

Can I page past the 50 orders it returns?

No. There is no cursor, offset or page parameter, and the limit is fixed in the query. If you need a full history, keep your own record as you create orders.