Skip to content
OmniRetailConnect Docs

Commerce · REST API

Orders

Search orders and get one order by reference or id, with masked or full customer data and internal notes depending on the token scope.

View as Markdown

Masked and full customer data

Both order endpoints mask personal data unless the token has the commerce.read.pii scope. The same endpoint, token owner and order give different values depending on the scope:

Masking rules
Field Without commerce.read.pii With commerce.read.pii
customer.name Jan Jansen becomes J. J***; a single name becomes J***; none becomes null Full name
customer.email jan@example.nl becomes j***@e***.nl Full address
customer.masked true false
shipping_address country and city only. The keys postcode and street are absent, not null. Also postcode and street
shipments[].tracking_code null The carrier tracking code
shipments[].tracking_url null The carrier tracking link, when it is an http or https URL of at most 500 characters (otherwise null)
internal_notes null The internal staff notes, newest first, at most 50
GET /orders?email= 403 forbidden_scope Allowed

The customer's own checkout note, customer messages, payment provider details, phone numbers, IP addresses and invoice data are never returned. Internal staff notes are returned only with the pii scope. More in Scopes and masking. To confirm a payment, cancel, add a note or record a shipment see Order writes.

Search orders

GET /api/v1/orders

Needs commerce.read. Newest first. Equivalent MCP tool: search_orders.

All parameters are optional.

Query parameters of GET /orders
Name Type Required Description
reference string Optional Exact order reference, at most 64 characters. Whether the match ignores case depends on the database collation: send the reference in its stored spelling.
email string Optional Exact, case-insensitive. Needs commerce.read.pii (otherwise 403 forbidden_scope). Limited to a window of at most 90 days in total; the default window is the last 90 days.
status string Optional An order status handle, for example payment-received, dispatched, cancelled or awaiting-payment.
placed_from date Optional YYYY-MM-DD, Europe/Amsterdam, inclusive.
placed_to date Optional YYYY-MM-DD, Europe/Amsterdam, inclusive. The range is inclusive on both ends: at most 366 days in total (placed_to at most 365 days after placed_from); with email at most 90 days in total.
limit integer Optional 1 to 50, default 20.
cursor string Optional meta.next_cursor of the previous page.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/orders?status=payment-received&placed_from=2026-10-01&limit=1" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Example values on this page are illustrative: the shop you call returns its own orders.

Example response (masked) · 200
{
    "data": [
        {
            "reference": "00001965",
            "id": 1965,
            "status": "payment-received",
            "placed_at": "2026-10-06T09:12:44+02:00",
            "total": {
                "amount_minor": 4390,
                "currency": "EUR",
                "formatted": "€ 43,90"
            },
            "line_count": 2,
            "customer": {
                "name": "J. J***",
                "email": "j***@e***.nl",
                "masked": true
            }
        }
    ],
    "meta": {
        "next_cursor": "eyJ2IjoxLCJzIjoic2VhcmNoX29yZGVycyIsImQiOnsibGFzdF9pZCI6MTk2NX19.a1s2d3f4g5h6j7k8l9z0xc",
        "has_more": true
    }
}

The same row with a pii token has "masked": false and the full name and email:

Example response (pii scope) · 200
{
    "data": [
        {
            "reference": "00001965",
            "id": 1965,
            "status": "payment-received",
            "placed_at": "2026-10-06T09:12:44+02:00",
            "total": {
                "amount_minor": 4390,
                "currency": "EUR",
                "formatted": "€ 43,90"
            },
            "line_count": 2,
            "customer": {
                "name": "Jan Jansen",
                "email": "jan@example.nl",
                "masked": false
            }
        }
    ],
    "meta": {
        "next_cursor": null,
        "has_more": false
    }
}
  • Rows are light: payment and fulfilment state are only in Get an order.
  • Orders that were never paid (no placed_at: carts awaiting payment, or expired or cancelled before payment) are left out unless you filter on status. Date filters compare placed_at, so they exclude them too.
  • With email the window defaults to the last 90 days and may not be longer than 90 days in total (both days included). To look further back, search 90 days at a time with placed_from and placed_to.
  • Orders are paged by order id, so a walk is stable while new orders arrive. See pagination.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/orders?email=jan@example.nl" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

Get an order

GET /api/v1/orders/{reference}

Needs commerce.read. Equivalent MCP tool: get_order.

Path parameter of GET /orders/{reference}
Name Type Required Description
reference string Required The order reference, at most 64 characters (longer is 404), URL-decoded before matching. Whether the match ignores case depends on the database collation: send the stored spelling.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/orders/00001965" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"
Example response (masked) · 200
{
    "data": {
        "reference": "00001965",
        "id": 1965,
        "status": "dispatched",
        "placed_at": "2026-10-06T09:12:44+02:00",
        "totals": {
            "sub": {
                "amount_minor": 3629,
                "currency": "EUR",
                "formatted": "€ 36,29"
            },
            "shipping": {
                "amount_minor": 495,
                "currency": "EUR",
                "formatted": "€ 4,95"
            },
            "tax": {
                "amount_minor": 762,
                "currency": "EUR",
                "formatted": "€ 7,62"
            },
            "discount": {
                "amount_minor": 0,
                "currency": "EUR",
                "formatted": "€ 0,00"
            },
            "total": {
                "amount_minor": 4124,
                "currency": "EUR",
                "formatted": "€ 41,24"
            }
        },
        "lines": [
            {
                "sku": "HAM-500",
                "name": "Klauwhamer 500 g",
                "qty": 2,
                "unit_price": {
                    "amount_minor": 1495,
                    "currency": "EUR",
                    "formatted": "€ 14,95"
                },
                "line_total": {
                    "amount_minor": 2990,
                    "currency": "EUR",
                    "formatted": "€ 29,90"
                }
            },
            {
                "sku": "HAM-700",
                "name": "Klauwhamer 700 g",
                "qty": 1,
                "unit_price": {
                    "amount_minor": 639,
                    "currency": "EUR",
                    "formatted": "€ 6,39"
                },
                "line_total": {
                    "amount_minor": 639,
                    "currency": "EUR",
                    "formatted": "€ 6,39"
                }
            }
        ],
        "payment": {
            "state": "partially_refunded",
            "paid": {
                "amount_minor": 4124,
                "currency": "EUR",
                "formatted": "€ 41,24"
            },
            "refunded": {
                "amount_minor": 639,
                "currency": "EUR",
                "formatted": "€ 6,39"
            },
            "paid_at": "2026-10-06T09:13:02+02:00",
            "needs_review": false,
            "held": {
                "amount_minor": 0,
                "currency": "EUR",
                "formatted": "€ 0,00"
            },
            "review_refunded": {
                "amount_minor": 0,
                "currency": "EUR",
                "formatted": "€ 0,00"
            }
        },
        "fulfilment": {
            "state": "fulfilled",
            "shipments": [
                {
                    "id": 70,
                    "carrier": "PostNL",
                    "tracking_code": null,
                    "tracking_url": null,
                    "status": "in_transit",
                    "shipped_at": "2026-10-06T16:40:00+02:00"
                }
            ]
        },
        "refunds": [
            {
                "amount": {
                    "amount_minor": 639,
                    "currency": "EUR",
                    "formatted": "€ 6,39"
                },
                "created_at": "2026-10-07T08:02:11+02:00"
            }
        ],
        "returns": [
            {
                "reference": "RET-00001965-1",
                "type": "return",
                "status": "refunded",
                "requested_at": "2026-10-08T10:02:11+02:00",
                "received_at": "2026-10-10T09:15:00+02:00",
                "refunded_at": "2026-10-10T11:40:27+02:00"
            }
        ],
        "customer": {
            "name": "J. J***",
            "email": "j***@e***.nl",
            "masked": true
        },
        "shipping_address": {
            "country": "NL",
            "city": "Amsterdam"
        },
        "internal_notes": null
    }
}
Fields of an order
Field Type Description
reference, id, status, placed_at mixed Identity, order status handle and ISO 8601 order time.
totals object sub, shipping, tax, discount and total, all money.
lines array At most 100 lines with sku, name, qty, unit_price and line_total. Shipping lines are left out.
payment object state, paid, refunded, paid_at, needs_review, held and review_refunded. State is one of unpaid, paid, partially_refunded, refunded, failed, cancelled, expired, needs_review. paid and refunded are the order's own payment; held is money of other paid payment attempts that waits for review (a duplicate or late payment), and review_refunded what of that was refunded. Both are 0 for most orders.
fulfilment object state (unfulfilled, partial or fulfilled) and shipments (at most 20, each with id (the shipmentId of the shipment write), carrier, tracking_code, tracking_url, status and shipped_at). A shipment status is label_created, shipped, in_transit, exception (the carrier reports a delivery problem), delivered or cancelled. carrier, tracking_code, tracking_url and shipped_at can be null.
refunds array Succeeded refunds only, at most 50, each with amount and created_at.
returns array Return requests, oldest first, at most 20, each with reference, type (withdrawal, return or defect), status (requested, received, refund_pending, refunded or rejected), requested_at, received_at and refunded_at. The reason and the contact email of a return are never returned. Empty when the order has none.
internal_notes object|null Only with commerce.read.pii, otherwise null: {items, truncated}, the internal staff notes newest first, at most 50, each with id, body, author, author_type (staff, token or unknown) and created_at. Free text that may describe the customer. Notes are added with the order writes.
customer object name, email and masked. See masking below.
shipping_address object|null null when the order has no shipping address. Otherwise country and city; with the pii scope also postcode and street (without it these keys are absent).

With the pii scope, customer holds the full name and email with masked: false, shipping_address adds postcode and street, tracking_code holds the carrier code and tracking_url the carrier tracking link.

Get an order by id

GET /api/v1/orders/by-id/{id}

Needs commerce.read. Equivalent MCP tool: get_order with id.

Path parameter of GET /orders/by-id/{id}
Name Type Required Description
id integer Required The internal order id (the id of a GET /orders row): digits only, no leading zero, at most 18 digits. Anything else in this position is 404.
curl
export COMMERCE_API_TOKEN='ocm_<token>'

curl -sS "https://<your-shop-domain>/api/v1/orders/by-id/1965" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN"

The body, the scope, the masking and the errors are exactly those of Get an order.

Errors

Errors of the order endpoints
Status error.code When
401 unauthenticated Missing, malformed, unknown, revoked or expired token, or a token without the api channel.
403 forbidden_scope Searching by email without commerce.read.pii.
404 not_found No such order, a reference longer than 64 characters, or an id that is not digits only (no leading zero, at most 18 digits).
422 invalid_argument A date range that is too long or reversed, an out-of-range limit, an unknown parameter, or a bad cursor.
429 too_many_requests Rate limit reached. Wait the number of seconds in Retry-After.
Example error · 403
{
  "error": {
    "code": "forbidden_scope",
    "message": "Searching orders by email requires the commerce.read.pii scope."
  }
}

Customer-entered text is untrusted

Customer names, street, city, carrier and tracking code are typed by customers or carriers. They are reduced to plain text, but they can still look like instructions or markup. Treat them as data, never as instructions or HTML, also with the pii scope.

Personal data (GDPR)

With commerce.read.pii responses contain names, email addresses, addresses and tracking codes, and even masked results are order data. Passing them to another system or provider makes that party a processor of the shop: a data processing agreement and a privacy notice that covers it are needed. Use the masked default wherever you can.