Commerce
The merchant's catalogue and the order lookup — the pull half of the commerce sync.
Two reads for a consumer application working on a connected merchant's behalf:
the product catalogue, and the "where is my order" lookup. Both require
commerce:read.
Products
GET /api/v1/commerce/productsThe catalogue with each product's variants nested, keyset-paginated on
(updated_at, product_id) ascending.
A webhook is a tickle, not the data
catalog.updated tells you a pull is worth making; it never carries the
catalogue. Pass the newest updated_at you have seen as updated_since and
walk forward — the ascending order is what keeps a cursor valid no matter how
many products change while you page.
Orders
GET /api/v1/commerce/ordersAt least one of order_ref, email or phone is required. A call with
none answers 400: this is a lookup on a named shopper's behalf, not a dump of
the merchant's order history.
No customer identity comes back. You already hold whichever contact you searched with, so echoing it would tell you nothing — and not returning it is what keeps the route from becoming an enumeration tool.
Live store reads
GET /api/v1/commerce/live/products
GET /api/v1/commerce/live/ordersThe two reads above serve Atribu's copy of the store. These two read the
connected Shopify store live, from Shopify's Admin API, in the request:
use them for an initial import and a periodic reconcile, and take deltas from
the shopify webhook events. Both require commerce:live, which the
commerce connect scope grants together with commerce:read.
- Products carry every variant, read to the end —
variant_id(the id a cart permalink takes),options,price,compare_at_price,inventory_quantity,inventory_policy,tracked— in every status (active,archived,draft). - Orders come in every status, with
financial_statusandfulfillment_statusin Shopify's REST spelling (the values theorders/*webhooks carry),attributes(the cart / note attributes, e.g.vitrina_order), and every line with itsvariant_idandquantity.
Both sort by Shopify's updated_at ascending. Pass updated_since to read a
delta and walk pagination.cursor until has_next is false — the cursor
carries the filter and the store, so send it alone. meta.connection_id and
meta.shop name the store; a profile with several stores needs
connection_id. A throttled Shopify response waits for its cost budget and
retries rather than failing the page.
Personal data
Every order from GET /api/v1/commerce/live/orders carries the buyer's
email and phone. That is why commerce:live is its own scope.
| Status | Code | When |
|---|---|---|
404 | not_found | The profile has no live Shopify connection |
400 | invalid_parameter | Bad updated_since; several stores and no connection_id |
409 | connection_revoked | Shopify no longer accepts the store's grant — reconnect it |
502 | provider_error | Shopify failed the read (its own message) |
503 | service_unavailable | The token could not be refreshed right now; retry |
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/commerce/live/orders | Read a connected store's orders live |
GET | /api/v1/commerce/live/products | Read a connected store's catalogue live |
GET | /api/v1/commerce/orders | Look an order up |
GET | /api/v1/commerce/products | List commerce products |
Generated from openapi.json. The full request and response schema for every operation is in the OpenAPI document.