> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omnicommerce.sg/llms.txt
> Use this file to discover all available pages before exploring further.

# Orders API

> List, create, and patch workspace and agentic orders.

Use the Developer Platform orders endpoints to list workspace orders with the same filters as the UI, create native `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](/guides/settlements)
(`POST /api/v1/settlements`).

## Authentication

Send an OmniCommerce API key or OAuth client-credentials token:

```http theme={null}
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json
```

| Endpoint                         | Required scope |
| -------------------------------- | -------------- |
| `GET /api/v1/orders`             | `orders:read`  |
| `POST /api/v1/orders`            | `orders:write` |
| `PATCH /api/v1/orders/{orderId}` | `orders:write` |

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):

| Status      | Meaning                             |
| ----------- | ----------------------------------- |
| `pending`   | Unpaid / not ready to fulfill       |
| `to_ship`   | Paid / ready for seller fulfillment |
| `shipped`   | In transit / handed to carrier      |
| `completed` | Delivered / finished                |
| `return`    | Return or refund in progress / done |
| `canceled`  | Canceled or invalid                 |

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:

| Field                                | Level             | Meaning                                                                                         |
| ------------------------------------ | ----------------- | ----------------------------------------------------------------------------------------------- |
| `currency`                           | Order and/or line | ISO-4217 code (for example `SGD`, `MYR`, `PHP`, `HKD`).                                         |
| `country`                            | Order             | Marketplace country. When empty, derived from order currency when unambiguous (see below).      |
| `totalAmount`                        | Order             | Order total. On create, computed from line paid/unit prices × quantity when omitted.            |
| `shippingFee`                        | Order             | Shipping fee.                                                                                   |
| `discountAmount`                     | Order             | Order-level discount.                                                                           |
| `price`                              | Line              | **Alias for `unitPrice`** (list / original unit price). Prefer `unitPrice` on new integrations. |
| `unitPrice`                          | Line              | Per-unit **list / sticker** price before line discounts.                                        |
| `paidPrice`                          | Line              | Per-unit amount **actually charged** after discounts (preferred for GMV and totals).            |
| `costPrice`                          | Line              | Unit **COGS** (not a selling price). See below.                                                 |
| `costPriceCurrency` / `costCurrency` | Line              | Currency of the raw `costPrice` (e.g. `USD`).                                                   |
| `costPriceFxRate`                    | Line              | Manual FX: units of target per 1 unit of cost currency.                                         |
| `costPriceFxTo`                      | Line              | Target currency (defaults to line/order currency).                                              |
| `costPriceFxAsOf`                    | Line              | `YYYY-MM` or `YYYY-MM-DD` for Frankfurter auto-rate.                                            |

### `price` vs `unitPrice` vs `paidPrice`

These three are **selling** prices on the line (per unit). They are **not** COGS and **not** marketplace fees.

| Field           | Meaning                                           | When to send                                               |
| --------------- | ------------------------------------------------- | ---------------------------------------------------------- |
| **`unitPrice`** | List / original unit price (before line discount) | Optional; useful for “was / now” or discount reporting     |
| **`price`**     | **Same as `unitPrice`** (legacy alias)            | Optional; prefer `unitPrice` for new code                  |
| **`paidPrice`** | What the buyer **paid per unit** after discounts  | **Preferred** for totals, GMV, and return refund estimates |

**How Omni picks a number for line money**

1. If **`paidPrice`** is set → use `paidPrice × quantity`
2. Else if **`unitPrice`** or **`price`** is set → use that × quantity
3. Else if **`productId`** is set on create → may resolve from the product catalog

**You do not need all three.** For most agentic / Zalora-style integrations, send **one** selling price:

```json theme={null}
{ "quantity": 1, "paidPrice": 80 }
```

or, if you only have a single price field in your system:

```json theme={null}
{ "quantity": 1, "price": 80 }
```

Use **both** list and paid when they differ:

```json theme={null}
{
  "quantity": 1,
  "unitPrice": 100,
  "paidPrice": 80
}
```

| Do not confuse with |                                               |
| ------------------- | --------------------------------------------- |
| `totalAmount`       | Order-level total (all lines + fees you pass) |
| `costPrice`         | Unit COGS (cost of goods), not revenue        |

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`:

1. OmniCommerce **upserts** `product_data.costPrice` and `product_data.costPriceCurrency` on that product (**after FX**, in local/target currency).
2. The value is **snapshotted** on the order line as `metadata.costPrice`; this snapshot is canonical for historical order/P\&L economics.

Rules:

* `costPrice` belongs on the **`items[]`** array (per line), not on the order root.
* **Omitted `costPrice` never 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 `costPrice` is **explicitly present** on the request.

### Cost currency and FX

P\&L treats `costPrice` 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:

```json theme={null}
{
  "costPrice": 120,
  "costPriceCurrency": "USD",
  "costPriceFxTo": "MYR",
  "costPriceFxRate": 4.45,
  "costPriceFxAsOf": "2026-07"
}
```

* Rate meaning: **1 USD = 4.45 MYR** → stored local cost `534`.
* If `costPriceCurrency` differs from the target and **`costPriceFxRate` is omitted**, Omni fetches the rate from [Frankfurter](https://api.frankfurter.dev/v1/latest?base=USD) (`getFxQuote` / ECB daily rates). Optional `costPriceFxAsOf` pins a historical date (`YYYY-MM-DD` or month `YYYY-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.
* `costPriceFxTo` must match the effective line/order currency so COGS and revenue remain comparable.
* Cost currency and FX fields require an explicit `costPrice` on 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 `400` **before** the order is written — retry with `costPriceFxRate`.

### Catalog fallbacks on create

When line `price` / `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

```http theme={null}
GET /api/v1/orders
```

### Query parameters

These match the workspace Orders listing page (`/app/{organizationId}/orders`):

| Parameter        | Description                                                                                                                             |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `organizationId` | Organization scope for session auth. Optional for single-org OAuth/API keys.                                                            |
| `q`              | Search order number, external order ID, buyer fields, and line-item product/SKU fields.                                                 |
| `status`         | Status group filter: `to_ship`, `shipped`, `completed`, `pending`, `return`, `canceled`.                                                |
| `marketplace`    | Comma-separated platforms, for example `shopee,lazada,tiktok`.                                                                          |
| `country`        | Comma-separated normalized country codes.                                                                                               |
| `store`          | Comma-separated marketplace account IDs.                                                                                                |
| `sortBy`         | Comma-separated sort tokens (multi-sort, max 6). Default: `created_desc`. Aliases: `latest` → `created_desc`, `oldest` → `created_asc`. |
| `dateFrom`       | Inclusive created-date lower bound (`YYYY-MM-DD`).                                                                                      |
| `dateTo`         | Inclusive created-date upper bound (`YYYY-MM-DD`).                                                                                      |
| `offset`         | Zero-based pagination offset. Default `0`.                                                                                              |
| `limit`          | Page size. Default `20`, max `50`.                                                                                                      |

### Response

Each item includes order totals and line money fields when available:

```json theme={null}
{
  "items": [
    {
      "id": "00000000-0000-4000-8000-000000000001",
      "externalOrderId": "partner-order-1001",
      "status": "pending",
      "statusGroup": "pending",
      "marketplace": "agentic",
      "currency": "SGD",
      "totalAmount": 100,
      "totalLabel": "SGD 100.00",
      "totalQuantity": 2,
      "lines": [
        {
          "id": "line_1",
          "name": "Linen Wrap Dress",
          "quantity": 2,
          "sku": "DRESS-001",
          "productId": "11111111-1111-4111-8111-111111111111",
          "unitPrice": 50,
          "paidPrice": 50,
          "costPrice": 20,
          "currency": "SGD"
        }
      ]
    }
  ],
  "hasMore": false,
  "nextOffset": 20,
  "summary": {
    "totalCount": 1,
    "toShipCount": 0,
    "shippedCount": 0,
    "completedCount": 0,
    "pendingCount": 1,
    "returnCount": 0,
    "canceledCount": 0
  },
  "pagination": {
    "offset": 0,
    "limit": 20,
    "total": 1,
    "hasMore": false,
    "nextOffset": null,
    "maxLimit": 50,
    "defaultLimit": 20
  }
}
```

Line `costPrice` is read from the order-line metadata snapshot (`metadata.costPrice`).

## Create order

```http theme={null}
POST /api/v1/orders
```

Creates a native `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

```json theme={null}
{
  "organizationId": "org_123",
  "externalOrderId": "partner-order-1001",
  "externalOrderNumber": "PO-1001",
  "status": "to_ship",
  "currency": "SGD",
  "totalAmount": 100,
  "shippingFee": 5,
  "discountAmount": 0,
  "country": "SG",
  "buyer": {
    "name": "Alex Buyer",
    "email": "alex@example.com",
    "phone": "+6590000000"
  },
  "shippingAddress": {
    "line1": "123 Market Street",
    "city": "Singapore",
    "country": "SG"
  },
  "items": [
    {
      "productId": "11111111-1111-4111-8111-111111111111",
      "productName": "Linen Wrap Dress",
      "sku": "DRESS-001",
      "quantity": 2,
      "price": 50,
      "unitPrice": 50,
      "paidPrice": 50,
      "costPrice": 20,
      "currency": "SGD",
      "status": "to_ship"
    }
  ],
  "metadata": {
    "sourceSystem": "partner-erp"
  }
}
```

| Field                         | Required | Notes                                                                                                                                                                                                                                                                 |
| ----------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `items`                       | Yes      | At least one line item (max 100).                                                                                                                                                                                                                                     |
| `items[].productName`         | Yes      | Display name for the line.                                                                                                                                                                                                                                            |
| `items[].quantity`            | Yes      | Positive integer.                                                                                                                                                                                                                                                     |
| `items[].externalOrderItemId` | No       | Stable line id for upserts and recon. Prefer partner per-SKU row id or marketplace item id. Generated as `{externalOrderId}-{index}` when omitted.                                                                                                                    |
| `items[].productId`           | No       | Product UUID. Enables catalog fallbacks and product `costPrice` upsert.                                                                                                                                                                                               |
| `items[].price`               | No       | Alias for `unitPrice`.                                                                                                                                                                                                                                                |
| `items[].unitPrice`           | No       | Per-unit list price. Falls back to product catalog when omitted with `productId`.                                                                                                                                                                                     |
| `items[].paidPrice`           | No       | Per-unit paid price; preferred for totals when set.                                                                                                                                                                                                                   |
| `items[].costPrice`           | No       | Unit COGS. With `productId`, upserts product `product_data.costPrice`. Omitted value never clears existing cost.                                                                                                                                                      |
| `items[].sku` / `sellerSku`   | No       | SKU fields. `sellerSku` is used as a secondary Zalora recon match when unique on the order.                                                                                                                                                                           |
| `items[].currency`            | No       | Line currency; falls back to order then product.                                                                                                                                                                                                                      |
| `items[].status`              | No       | Line status (canonical enum). Defaults to order status.                                                                                                                                                                                                               |
| `status`                      | No       | Canonical enum (default `pending`).                                                                                                                                                                                                                                   |
| `currency`                    | No       | Order currency (ISO-4217). Prefer **market** currency for marketplace recon (e.g. PHP for Zalora PH, not catalog SGD).                                                                                                                                                |
| `country`                     | No       | Marketplace country (`SG`, `singapore`, `PH`, `philippines`, …). When omitted/empty, Omni derives from order currency when unambiguous (see [Country from currency](#country-from-currency)). Shipping address `country` is used when top-level `country` is omitted. |
| `externalOrderId`             | No       | Stable upsert key (unique per org + agentic). For marketplace recon use the marketplace order number. Generated when omitted.                                                                                                                                         |
| `externalOrderNumber`         | No       | Customer-facing number; defaults to `externalOrderId`.                                                                                                                                                                                                                |
| `platform`                    | No       | Always `agentic` for this endpoint.                                                                                                                                                                                                                                   |
| `totalAmount`                 | No       | When omitted, recomputed from **all** lines after upsert (safe for sequential multi-SKU posts). When set, stored as provided.                                                                                                                                         |
| `shippingFee`                 | No       | Shipping fee.                                                                                                                                                                                                                                                         |
| `discountAmount`              | No       | Order-level discount.                                                                                                                                                                                                                                                 |
| `orderCreatedAt`              | No       | ISO 8601 datetime with timezone. Stored as `order_created_at` (listing and analytics date filters). Defaults to now. Re-posting the same `externalOrderId` does **not** overwrite an existing created date — use PATCH.                                               |

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 **order `country` is empty**, Omni may set country from **order currency** (and the same rule applies on PATCH and Zalora recon apply). Explicit `country` always wins.

| Currency | Derived country (stored canonical name) |
| -------- | --------------------------------------- |
| `SGD`    | `singapore`                             |
| `MYR`    | `malaysia`                              |
| `PHP`    | `philippines`                           |
| `HKD`    | `hong kong`                             |
| `THB`    | `thailand`                              |
| `VND`    | `vietnam`                               |
| `IDR`    | `indonesia`                             |
| `TWD`    | `taiwan`                                |
| `BRL`    | `brazil`                                |
| `USD`    | *(not derived — ambiguous)*             |

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 agentic `external_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:

1. Always use the **marketplace order id** as `externalOrderId` (not your per-SKU order number).
2. Put your unique per-SKU id on `items[].externalOrderItemId`.
3. You may POST **one line at a time** with the same `externalOrderId` — lines upsert onto the same Omni order.
4. **Omit `totalAmount`** when appending lines so Omni recomputes the order total from all lines. Send `totalAmount` only when you know the full cart total.

Do **not** invent composite `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 `externalOrderItemId`s (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):

| Your order # | Marketplace order id | SKU     | Paid   |
| ------------ | -------------------- | ------- | ------ |
| `ORD-9001`   | `3012345678`         | `SKU-A` | 50 SGD |
| `ORD-9002`   | `3012345678`         | `SKU-B` | 40 SGD |

**Wrong** — two Omni orders (or same `externalOrderId` on two shells → `409`):

```http theme={null}
POST /api/v1/orders
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "externalOrderId": "ORD-9001",
  "currency": "SGD",
  "items": [
    {
      "externalOrderItemId": "ORD-9001",
      "sku": "SKU-A",
      "productName": "Item A",
      "quantity": 1,
      "paidPrice": 50
    }
  ]
}
```

```http theme={null}
POST /api/v1/orders
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "externalOrderId": "ORD-9002",
  "currency": "SGD",
  "items": [
    {
      "externalOrderItemId": "ORD-9002",
      "sku": "SKU-B",
      "productName": "Item B",
      "quantity": 1,
      "paidPrice": 40
    }
  ]
}
```

Zalora report order `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:

```http theme={null}
POST /api/v1/orders
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "externalOrderId": "3012345678",
  "externalOrderNumber": "3012345678",
  "currency": "SGD",
  "status": "to_ship",
  "items": [
    {
      "externalOrderItemId": "ORD-9001",
      "sku": "SKU-A",
      "sellerSku": "SKU-A",
      "productName": "Item A",
      "quantity": 1,
      "paidPrice": 50
    }
  ]
}
```

First response (`201`, `created: true`):

```json theme={null}
{
  "ok": true,
  "status": "completed",
  "data": {
    "orderId": "11111111-1111-4111-8111-111111111111",
    "externalOrderId": "3012345678",
    "created": true,
    "platform": "agentic",
    "totalAmount": 50
  }
}
```

```http theme={null}
POST /api/v1/orders
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "externalOrderId": "3012345678",
  "externalOrderNumber": "3012345678",
  "currency": "SGD",
  "status": "to_ship",
  "items": [
    {
      "externalOrderItemId": "ORD-9002",
      "sku": "SKU-B",
      "sellerSku": "SKU-B",
      "productName": "Item B",
      "quantity": 1,
      "paidPrice": 40
    }
  ]
}
```

Second response (`200`, `created: false` — same order, second line appended):

```json theme={null}
{
  "ok": true,
  "status": "completed",
  "data": {
    "orderId": "11111111-1111-4111-8111-111111111111",
    "externalOrderId": "3012345678",
    "created": false,
    "platform": "agentic",
    "totalAmount": 90
  }
}
```

Result in Omni:

| Field                        | Value                                      |
| ---------------------------- | ------------------------------------------ |
| One order `externalOrderId`  | `3012345678` (matches Zalora order number) |
| Line 1 `externalOrderItemId` | `ORD-9001` (your unique row)               |
| Line 2 `externalOrderItemId` | `ORD-9002`                                 |
| `totalAmount`                | `90` (recomputed from both lines)          |

You can also send **both lines in one request** if your push job can group by marketplace order id:

```http theme={null}
POST /api/v1/orders
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "externalOrderId": "3012345678",
  "externalOrderNumber": "3012345678",
  "currency": "SGD",
  "status": "to_ship",
  "items": [
    {
      "externalOrderItemId": "ORD-9001",
      "sku": "SKU-A",
      "sellerSku": "SKU-A",
      "productName": "Item A",
      "quantity": 1,
      "paidPrice": 50
    },
    {
      "externalOrderItemId": "ORD-9002",
      "sku": "SKU-B",
      "sellerSku": "SKU-B",
      "productName": "Item B",
      "quantity": 1,
      "paidPrice": 40
    }
  ]
}
```

### Response

```json theme={null}
{
  "ok": true,
  "apiVersion": "developer_v1",
  "operationId": "8f7e8f8f-2b9e-46e6-a2c0-50d0f4612b2d",
  "status": "completed",
  "warnings": [],
  "recommendations": [],
  "links": {
    "order": "/api/v1/orders?organizationId=org_123&q=partner-order-1001"
  },
  "data": {
    "orderId": "00000000-0000-4000-8000-000000000001",
    "externalOrderId": "partner-order-1001",
    "created": true,
    "platform": "agentic",
    "totalAmount": 100
  }
}
```

| Response field    | Notes                                                                                                      |
| ----------------- | ---------------------------------------------------------------------------------------------------------- |
| `orderId`         | OmniCommerce order UUID. Stable across re-posts of the same `externalOrderId`.                             |
| `externalOrderId` | Echo of the upsert key.                                                                                    |
| `created`         | `true` on first insert (`201`); `false` on upsert (`200`).                                                 |
| `platform`        | Always `agentic` for this endpoint.                                                                        |
| `totalAmount`     | Final order total after create/upsert (explicit request value, or recomputed from all lines when omitted). |

## Update order

```http theme={null}
PATCH /api/v1/orders/{orderId}
```

Updates a native **agentic** order. Supports external order identifiers, status, created date, currency, country, money fields, metadata, and line-item upserts (`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](#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

| Parameter | Description                                  |
| --------- | -------------------------------------------- |
| `orderId` | OmniCommerce order UUID from create or list. |

### Request body

Status-only:

```json theme={null}
{
  "organizationId": "org_123",
  "status": "shipped",
  "metadata": {
    "trackingNumber": "SG123456789",
    "carrier": "Ninja Van"
  }
}
```

Money, currency, and line pricing (status optional):

```json theme={null}
{
  "organizationId": "org_123",
  "currency": "SGD",
  "totalAmount": 120.5,
  "shippingFee": 5,
  "items": [
    {
      "externalOrderItemId": "partner-order-1001-1",
      "price": 110,
      "costPrice": 45,
      "currency": "SGD"
    }
  ]
}
```

Correct created date:

```json theme={null}
{
  "organizationId": "org_123",
  "orderCreatedAt": "2026-03-15T08:00:00.000Z"
}
```

Repair identifiers on an imported order:

```json theme={null}
{
  "organizationId": "org_123",
  "externalOrderId": "MY-223347629SG",
  "externalOrderNumber": "MY-223347629SG",
  "items": [
    {
      "currentExternalOrderItemId": "dev-order-1",
      "externalOrderItemId": "10817558"
    }
  ]
}
```

| Field                          | Required               | Notes                                                                                                                                    |
| ------------------------------ | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| *(at least one mutable field)* | Yes                    | Empty body is rejected.                                                                                                                  |
| `externalOrderId`              | No                     | Replacement stable order identifier. Must be unique among agentic orders in the organization.                                            |
| `externalOrderNumber`          | No                     | Replacement customer-facing order number.                                                                                                |
| `status`                       | No                     | Canonical enum. Optional when patching money/lines/metadata.                                                                             |
| `currency`                     | No                     | Order currency (ISO-4217).                                                                                                               |
| `country`                      | No                     | Marketplace country. When omitted and stored country is empty, derived from order currency when unambiguous. Explicit value always wins. |
| `totalAmount`                  | No                     | Order total. When omitted and line prices change, recomputed from lines.                                                                 |
| `shippingFee`                  | No                     | Shipping fee.                                                                                                                            |
| `discountAmount`               | No                     | Order-level discount.                                                                                                                    |
| `orderCreatedAt`               | No                     | ISO 8601 datetime with timezone. Overwrites stored `order_created_at`. Omitted value is left unchanged.                                  |
| `organizationId`               | Session multi-org only | Optional for single-org API keys / OAuth clients.                                                                                        |
| `metadata`                     | No                     | Shallow merge into existing order metadata (does not replace the whole object).                                                          |
| `items`                        | No                     | Partial line updates (1–100).                                                                                                            |

### Line patch identity

Each `items[]` entry must identify a line with:

| Field                        | Notes                                                                                                                                                                                                                    |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `currentExternalOrderItemId` | Current line identifier. Use this selector when replacing `externalOrderItemId`.                                                                                                                                         |
| `externalOrderItemId`        | Selects the current line when `currentExternalOrderItemId` is omitted. When both are present, this is the replacement identifier. If no line matches and `productName` + `quantity` are set, a **new line is inserted**. |
| `productId`                  | Allowed when that product appears on exactly one line of the order.                                                                                                                                                      |

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 status `return` 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):

```http theme={null}
PATCH /api/v1/orders/{orderId}
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "items": [
    {
      "externalOrderItemId": "ORD-9001",
      "status": "return"
    }
  ]
}
```

Response includes roll-up fields:

| Field                    | Partial example                                   |
| ------------------------ | ------------------------------------------------- |
| `returnState`            | `partial`                                         |
| `hasReturnSignal`        | `true`                                            |
| `returnRefundAmount`     | ≥ revenue of returned line(s) (`paidPrice × qty`) |
| `includeInSalesMetrics`  | `true` (kept lines still count)                   |
| `status` / `statusGroup` | **Unchanged** unless you set top-level `status`   |

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):

```json theme={null}
{
  "items": [
    { "externalOrderItemId": "ORD-9001", "status": "return" },
    { "externalOrderItemId": "ORD-9002", "status": "return" }
  ]
}
```

When **all** lines are `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:

```json theme={null}
{ "status": "return" }
```

That top-level status **marks every line** `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:

| Field             | Meaning                                                                               |
| ----------------- | ------------------------------------------------------------------------------------- |
| `paymentReceived` | Sum of non-returned line payment-received; null until every active line is reconciled |
| `orderCogs`       | Sum of sold line `metadata.costPrice × qty`; null while any active line lacks COGS    |
| `orderProfit`     | `paymentReceived − orderCogs`; null if either input is incomplete                     |
| `orderMargin`     | `orderProfit / paymentReceived` (null if no payment)                                  |

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 order `3012345678` with line `ORD-9001`, append line `ORD-9002` without re-posting the whole cart:

```http theme={null}
PATCH /api/v1/orders/11111111-1111-4111-8111-111111111111
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "items": [
    {
      "externalOrderItemId": "ORD-9002",
      "productName": "Item B",
      "sku": "SKU-B",
      "sellerSku": "SKU-B",
      "quantity": 1,
      "paidPrice": 40,
      "currency": "SGD"
    }
  ]
}
```

Response includes recomputed `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:

```http theme={null}
PATCH /api/v1/orders/11111111-1111-4111-8111-111111111111
Authorization: Bearer $OMNI_API_KEY
Content-Type: application/json

{
  "items": [
    {
      "externalOrderItemId": "ORD-9002",
      "paidPrice": 38,
      "quantity": 2
    }
  ]
}
```

### Response

```json theme={null}
{
  "ok": true,
  "apiVersion": "developer_v1",
  "operationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "completed",
  "warnings": [],
  "recommendations": [],
  "links": {
    "order": "/api/v1/orders?organizationId=org_123&q=partner-order-1001"
  },
  "data": {
    "orderId": "00000000-0000-4000-8000-000000000001",
    "externalOrderId": "partner-order-1001",
    "externalOrderNumber": "PO-1001",
    "platform": "agentic",
    "previousStatus": "to_ship",
    "status": "shipped",
    "statusGroup": "shipped",
    "currency": "SGD",
    "totalAmount": 120.5,
    "shippingFee": 5,
    "discountAmount": 0,
    "orderCreatedAt": "2026-03-15T08:00:00.000Z",
    "changed": true,
    "changedFields": [
      "status",
      "currency",
      "totalAmount",
      "items.unitPrice",
      "items.costPrice"
    ]
  }
}
```

`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` — missing `orders:read` or `orders:write` scope.
* `404` — order not found for this organization (`PATCH` only).
* `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](/quickstart)
* [OpenAPI reference](/api-reference)
* [Authentication](/authentication)
* Agentic checkout endpoints: `POST /api/v1/checkout-sessions` in the [API reference](/api-reference)
