agentic orders from external systems, and patch agentic order fields (status, created date, money, currency, and line items).
Settlements (fees, settlement status, marketplace net, and payment received) are
not order fields. Use the Settlements API
(POST /api/v1/settlements).
Authentication
Send an OmniCommerce API key or OAuth client-credentials token:
OAuth clients must include the scope on the client. API keys created from organization settings receive all developer scopes by default.
Session-authenticated calls must include
organizationId in the query (GET) or JSON body (POST/PATCH).
Order status enum
Canonical statuses for create and patch (also used as listing filter groups):
Marketplace-imported orders may store platform-specific raw status strings; the developer write APIs only accept this enum.
Pricing fields
Money fields on orders and line items:price vs unitPrice vs paidPrice
These three are selling prices on the line (per unit). They are not COGS and not marketplace fees.
How Omni picks a number for line money
- If
paidPriceis set → usepaidPrice × quantity - Else if
unitPriceorpriceis set → use that × quantity - Else if
productIdis set on create → may resolve from the product catalog
These field descriptions are also published on OpenAPI / Redocly (
POST/PATCH /api/v1/orders line schemas).
costPrice (product-owned)
costPrice is not a first-class order table column. Its reusable catalog value is stored at:
products.product_data.costPrice
When you send items[].costPrice with a productId:
- OmniCommerce upserts
product_data.costPriceandproduct_data.costPriceCurrencyon that product (after FX, in local/target currency). - The value is snapshotted on the order line as
metadata.costPrice; this snapshot is canonical for historical order/P&L economics.
costPricebelongs on theitems[]array (per line), not on the order root.- Omitted
costPricenever clears an existing product or line cost. - When omitted on create with
productId, the line snapshot may still resolve from the product catalog if a cost is already stored there. - Product cost is only written when
costPriceis explicitly present on the request.
Cost currency and FX
P&L treatscostPrice as the same unit as order revenue. When invoice COGS is in another currency (e.g. USD) and the order is MYR/SGD/PHP/HKD:
- Rate meaning: 1 USD = 4.45 MYR → stored local cost
534. - If
costPriceCurrencydiffers from the target andcostPriceFxRateis omitted, Omni fetches the rate from Frankfurter (getFxQuote/ ECB daily rates). OptionalcostPriceFxAsOfpins a historical date (YYYY-MM-DDor monthYYYY-MM). - Line metadata keeps an audit trail:
sourceCostPrice,sourceCostCurrency,costPriceFxRate,costPriceFxFrom,costPriceFxTo,costPriceFxAsOf,costPriceFxSource(manual|frankfurter|fallback|identity). - Alias:
costCurrency=costPriceCurrency. - Manual seller rates always win over auto FX.
costPriceFxTomust match the effective line/order currency so COGS and revenue remain comparable.- Cost currency and FX fields require an explicit
costPriceon the same line. A currency-tagged catalog fallback is converted when its stored currency differs from the new line/order currency. - When no rate can be resolved (unsupported pair, FX provider unreachable), the request fails with
400before the order is written — retry withcostPriceFxRate.
Catalog fallbacks on create
When lineprice / unitPrice / paidPrice / currency are omitted and productId is provided, create resolves missing values from the product catalog (products.price, product_data, currency). Order total is then computed from the resolved line amounts when totalAmount is omitted.
List orders
Query parameters
These match the workspace Orders listing page (/app/{organizationId}/orders):
Response
Each item includes order totals and line money fields when available:costPrice is read from the order-line metadata snapshot (metadata.costPrice).
Create order
agentic order and line items through the same upsert path used by agentic checkout completion. Marketplace orders should continue to flow through marketplace import/webhook pipelines.
Request body
Re-posting the same
platform + externalOrderId pair updates the existing order and returns HTTP 200 with created: false. Lines upsert by externalOrderItemId.
Country from currency
When ordercountry is empty, Omni may set country from order currency (and the same rule applies on PATCH and Zalora recon apply). Explicit country always wins.
Workspace order listing and detail use the same fallback for display when stored country is empty.
Multi-SKU / one-row-per-SKU partners (Zalora recon)
Omni models one marketplace order → one Omni order. Marketplace recon (for example Zalora Item Transaction Reports) matches the report order number to agenticexternal_order_id, then matches lines by external_order_item_id or unique Seller SKU on that order.
If your system stores one row per SKU that share a marketplace order id:
- Always use the marketplace order id as
externalOrderId(not your per-SKU order number). - Put your unique per-SKU id on
items[].externalOrderItemId. - You may POST one line at a time with the same
externalOrderId— lines upsert onto the same Omni order. - Omit
totalAmountwhen appending lines so Omni recomputes the order total from all lines. SendtotalAmountonly when you know the full cart total.
externalOrderId values like MP-12345:SKU-A for uniqueness — recon will not match the marketplace order number.
If you already created separate Omni orders per SKU with different external ids, create/upsert again using the marketplace order id as externalOrderId and stable line externalOrderItemIds (preferred going forward). Do not set the same marketplace id on multiple Omni order shells via PATCH — that still returns 409.
Example: Zalora cart with 2 SKUs
Your system (one DB row per SKU):
Wrong — two Omni orders (or same
externalOrderId on two shells → 409):
3012345678 will not match either shell.
Right — one Omni order; your row ids become line ids. Same marketplace id on every push; omit totalAmount so Omni sums all lines:
201, created: true):
200, created: false — same order, second line appended):
You can also send both lines in one request if your push job can group by marketplace order id:
Response
Update order
externalOrderItemId, price / unitPrice / paidPrice / costPrice / currency / status / quantity, and more).
Marketplace orders are rejected with 409 — those fields are marketplace-owned. Use marketplace tooling or the workspace order-actions flow for them.
At least one of externalOrderId, externalOrderNumber, status, currency, country, totalAmount, shippingFee, discountAmount, metadata, orderCreatedAt, or items is required.
When country is omitted and the order’s stored country is empty, Omni fills country from the resulting order currency (same map as create — see Country from currency).
Line upserts: matching externalOrderItemId (or unique productId) updates the line. A new externalOrderItemId with productName + quantity inserts a line (multi-SKU append on an existing order). When totalAmount is omitted and lines change, Omni recomputes the total from all lines.
Path parameters
Request body
Status-only:Line patch identity
Eachitems[] entry must identify a line with:
Each patch line must also include at least one field to update (
price, unitPrice, paidPrice, costPrice, currency, status, quantity, etc.), or productName + quantity when inserting a new line.
Omitted costPrice on a line patch does not clear product or line cost. Explicit costPrice with a resolved product id upserts the product catalog cost.
Partial vs full return (line status)
Canonical line/order statusreturn marks a return. Returns are line-level; order status is a roll-up.
Partial return — one of several lines returned (order stays sold for remaining lines):
P&L and product economics treat returned lines as sold qty 0. Ledger fees with
external_order_item_id attribute to that line; remaining fees pro-rate by sold line revenue.
Full return — every line returned (or set order status explicitly):
return and you did not send a different top-level status, Omni sets order status to return, returnState to full, and returnRefundAmount to at least the full order total so retained revenue is zero.
You can still force full-order return with:
return, zeros line paymentReceived, rolls up refund/sales flags, and restocks product inventory only when the line had inventory deducted on sale (metadata.inventoryDeductedAt). Line selection by productId works the same as by externalOrderItemId (must be unique on the order).
PATCH responses and each GET /api/v1/orders item surface unit-economics fields:
Optional per-line refund override: set
items[].metadata.returnRefundAmount before/with the status patch; roll-up prefers that over paidPrice × qty.
Example: append a second SKU via PATCH
After creating order3012345678 with line ORD-9001, append line ORD-9002 without re-posting the whole cart:
totalAmount (e.g. 90 when the first line was 50). Omit top-level totalAmount so Omni sums all lines.
Update an existing line (same id) by sending only the fields to change:
Response
changed is false when the request is a no-op. changedFields lists what actually changed.
Errors
400— invalid JSON, validation failure, unknown status, missing patch fields, or line identity failure.401— missing or invalid bearer token / session.403— missingorders:readororders:writescope.404— order not found for this organization (PATCHonly).409— updates not supported for marketplace-owned orders, or an external identifier is already in use.429— developer API rate limit exceeded.500— unexpected server failure.
Related docs
- Developer Platform
- OpenAPI reference
- Authentication
- Agentic checkout endpoints:
POST /api/v1/checkout-sessionsin the API reference