Skip to content
OmniRetailConnect Docs

Commerce · REST API

Order writes

Confirm an offline payment, cancel an unpaid order, add internal notes and record shipments with tracking, without moving money.

View as Markdown

Overview

Five endpoints let an integration work an order the way staff do on the order page, without ever moving money. They need commerce.write.orders together with commerce.read (edit permission Orders, Bestellingen, in Settings > API tokens; a token with it lives at most 30 days). Each is the MCP tool of the same name, described in Order tools, with the same data, the same change log (channel api) and the same write limit. The order's activity trail (the Activiteit tab in the admin) names the token. The order id is the internal id of GET /orders; it is in the URL and must not be in the body.

No refunds, no paid cancellations

These endpoints never move money. There is no refund and no cancellation of a paid order in this API. Internal notes and tracking codes can hold personal data: they are only returned with commerce.read.pii and are never written to the change log. See Scopes and masking.

Confirm an offline payment

POST /api/v1/orders/{id}/confirm-payment

Needs commerce.write.orders. Equivalent MCP tool: confirm_offline_payment.

No body. Records that the full amount of an unpaid draft order (status awaiting-payment) was received outside the payment provider, for example by bank transfer. The order is placed (status payment-offline), its stock is committed and, after the commit, the customer gets the order confirmation and the shop its notification. The order's activity trail names the token as approver.

  • Result: id, reference, fulfilment_state, status, placed_at, payment_state, changed, changes.
  • An order this already confirmed answers 200 with changed: false and sends no second mail.
  • Any other order (paid through the provider, cancelled, held for payment review) is 409. So is a draft whose online payment is still open at the provider; staff in the admin may still confirm a bank transfer next to an abandoned online attempt, a token may not.

Cancel an unpaid order

POST /api/v1/orders/{id}/cancel

Needs commerce.write.orders. Equivalent MCP tool: cancel_unpaid_order.

Body: reason, one of customer_request, payment_not_received, duplicate_order, suspected_fraud or other. Cancels an unpaid draft order and releases its stock hold. This cannot be undone. The result is the same as for confirming.

  • An already cancelled order answers 200 with changed: false.
  • A draft held for payment review (money may have arrived: settle it on the order page), a draft whose online payment is still open, and a placed or paid order are 409.

Add an internal note

POST /api/v1/orders/{id}/notes

Needs commerce.write.orders. Equivalent MCP tool: add_order_note.

Body: body, up to 2000 characters. Adds an internal note for staff, shown on the order's activity trail and never to the customer or in a customer mail. It is stored as the text that was sent: never interpreted as HTML (angle brackets stay as they are, every view escapes them), control characters removed, line breaks kept. Result: order (id, reference, fulfilment_state), note (id, body as stored, author, author_type token, created_at), changed (always true), changes.

  • Not idempotent. Every call adds a note. Do not repeat a call that succeeded.
  • Read notes back in internal_notes of GET /orders/{reference}; that needs commerce.read.pii.
  • Notes may hold personal data. They are part of a data-subject access export and are deleted when the customer's data is erased. The change log keeps the note id, never the text.
Add an internal note
curl -sS -X POST "https://<your-shop-domain>/api/v1/orders/1042/notes" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"body": "Customer called: deliver after 17:00."}'

Record a shipment

POST /api/v1/orders/{id}/shipments

Needs commerce.write.orders. Equivalent MCP tool: add_shipment.

Records a shipment with track and trace for an order that is paid in full, through the staff rules. Without lines everything still to ship goes out. The order must not be cancelled, held for review or fully refunded, and a quantity may not exceed what is left (409 otherwise). The shipment names no staff member; the order's activity trail names the token.

Body fields of POST /orders/{id}/shipments
Name Type Required Description
idempotency_key string Required 1 to 64 of A-Z a-z 0-9 . _ : -. The same key with the same carrier, tracking code and lines answers changed: false and sends no second mail. The same key with other data, or on another order, is 409.
carrier string Required A plain carrier name.
tracking_code string Optional A plain code. Leave it out for letter post without track and trace.
tracking_url string Optional https only, at most 500 characters, and only together with a tracking_code.
shipped_at string Optional ISO 8601 with an offset, not in the future, not before the order was placed. Default now.
lines array Optional [{line_id, quantity}], physical lines of this order, each once. Omitted: everything still to ship.
notify_customer boolean Optional Default true: the customer gets the shipping mail after the commit. false records it silently.
Record a shipment
curl -sS -X POST "https://<your-shop-domain>/api/v1/orders/1042/shipments" \
  -H "Authorization: Bearer $COMMERCE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"idempotency_key": "agent-1042-1", "carrier": "PostNL", "tracking_code": "3SABCD1234567", "tracking_url": "https://track.example.com/3SABCD1234567"}'

Result: order, shipment (id, carrier, tracking_code, tracking_url, status, shipped_at, notify_customer, lines), changed, changes. tracking_code and tracking_url are null without commerce.read.pii, as in GET /orders/{reference}. The change log keeps a fingerprint (sha256:...) of a changed code or link, never the value.

Correct the tracking of a shipment

PATCH /api/v1/orders/{id}/shipments/{shipmentId}

Needs commerce.write.orders. Equivalent MCP tool: update_shipment_tracking.

Send at least one field. Only those fields change; status, lines and dates stay, and no mail is sent. Equal values answer 200 with changed: false. A shipment of another order is 404; a cancelled shipment, or one created through a carrier integration (the carrier owns its tracking), is 409. shipmentId is fulfilment.shipments[].id of the order.

Body fields of PATCH /orders/{id}/shipments/{shipmentId}
Name Type Required Description
carrier string Optional A plain carrier name.
tracking_code string|null Optional A plain code. null removes it.
tracking_url string|null Optional https only, at most 500 characters, only together with a tracking code. null removes it.

Errors

Errors of the order write endpoints
Status error.code When
403 forbidden_scope The token does not have commerce.write.orders.
404 not_found Unknown order, or a shipment that belongs to another order.
409 conflict The order is not in a state the action allows (see the rules above); an idempotency_key already used with other data or on another order; a quantity above what is left to ship; a cancelled shipment or one made through a carrier integration on PATCH.
422 invalid_argument An empty note, an http tracking link, a tracking link without a tracking code, a reason that is not in the list, a body field that does not exist, or no field at all on PATCH.
429 too_many_requests The write limit. The write did not happen.