> ## 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.

# Order webhooks

> The unified OmniCommerce order event contract.

Order webhooks contain a complete, provider-neutral order snapshot after the
order header and SKU-level lines are committed. Buyer PII is not included.

Subscribe to these events with `POST /api/v1/webhooks`. See [Webhooks](/webhooks/overview)
for subscription setup, signature verification, and delivery behavior.

## Events

| Event                  | Emitted when                                             | `data` fields                                                                 |
| ---------------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `order.created`        | A canonical order and its initial lines are first stored | `order`                                                                       |
| `order.updated`        | One or more public non-status fields change              | `order`, `changedFields`                                                      |
| `order.status.changed` | The canonical status changes                             | `order`, previous/current canonical and marketplace statuses, `changedFields` |
| `order.deleted`        | An order is explicitly deleted or legally purged         | Minimal `order` tombstone with stable identity and `deletedAt`                |

Historical imports and unchanged provider duplicates do not emit lifecycle
events. If one marketplace notification changes status and other fields,
OmniCommerce emits one `order.status.changed` event with all changed paths.

Marketplace cancellation is a status transition to `canceled`, not an
`order.deleted` event.

## Identity and source

`subject.id` is the stable OmniCommerce order ID. Marketplace identity is
scoped by all of:

```text theme={null}
organizationId + marketplace + accountId + externalOrderId
```

`source.accountId` is the exact connected store that produced the order. A
missing account is never used as a wildcard. `providerEventId` and
`providerEventType` can be `null` when the provider does not supply them or when
the change comes from a scheduled sync or developer API call.

`source.system` is one of:

| Value            | Meaning                                      |
| ---------------- | -------------------------------------------- |
| `marketplace`    | Verified marketplace push or notification    |
| `developer_api`  | Mutation through the OmniCommerce API        |
| `scheduled_sync` | Poll, import, refresh, or reconciliation job |
| `internal`       | Internal OmniCommerce workflow               |

## Statuses and money

`data.order.status` is one of:

```text theme={null}
pending | to_ship | shipped | completed | return | canceled
```

The original provider status remains in `marketplaceStatus`. Monetary values
are objects with a decimal string and ISO 4217 currency:

```json theme={null}
{
  "amount": "149.9",
  "currency": "SGD"
}
```

Every total in one order snapshot uses the order currency. Do not parse money
through binary floating point when exact accounting behavior matters.

| Total      | Meaning                                                             |
| ---------- | ------------------------------------------------------------------- |
| `subtotal` | Canonical merchandise subtotal: `total - shipping + discount - tax` |
| `discount` | Positive discount amount deducted from the order                    |
| `shipping` | Order-level shipping amount                                         |
| `tax`      | Order-level tax amount                                              |
| `total`    | Canonical order total reported by OmniCommerce                      |

## Order snapshot

| Field                 | Type                 | Description                                  |
| --------------------- | -------------------- | -------------------------------------------- |
| `id`                  | string               | Stable OmniCommerce order ID                 |
| `marketplace`         | string               | Canonical marketplace key                    |
| `accountId`           | string or `null`     | Exact connected marketplace account          |
| `externalOrderId`     | string               | Marketplace or source-system order ID        |
| `externalOrderNumber` | string or `null`     | Human-facing marketplace order number        |
| `status`              | canonical status     | Provider-neutral lifecycle status            |
| `marketplaceStatus`   | string or `null`     | Original provider status                     |
| `country`             | string or `null`     | Uppercase order/store country when known     |
| `currency`            | ISO 4217 string      | Currency shared by every total               |
| `totals`              | money object map     | Subtotal, discount, shipping, tax, and total |
| `itemCount`           | non-negative integer | Number of stored order lines                 |
| `quantity`            | non-negative integer | Sum of stored line quantities                |
| `items`               | line array           | PII-free SKU-level commercial outcomes       |
| `placedAt`            | ISO 8601 timestamp   | Marketplace/source order creation time       |
| `sourceUpdatedAt`     | timestamp or `null`  | Latest authoritative source update time      |
| `createdAt`           | ISO 8601 timestamp   | OmniCommerce record creation time            |
| `updatedAt`           | ISO 8601 timestamp   | OmniCommerce record update time              |

Each `items[]` entry includes stable internal and external line IDs, matched
`productId`, marketplace item and variant IDs, SKU aliases, name, status,
quantity, currency, unit/paid prices, line total, and historical `unitCost`.
Money is a decimal string plus currency; unavailable values are `null`.

`changedFields` contains public field paths such as `status`,
`marketplaceStatus`, `totals.total`, `itemCount`, `quantity`, or `items`. Internal sync
metadata and buyer PII never appear in this list.

## Example status change

```json theme={null}
{
  "schemaVersion": "2026-08-01",
  "id": "evt_7bf251d1b1bb4e5c82e44f9bdf653647",
  "type": "order.status.changed",
  "occurredAt": "2026-08-29T02:30:00.000Z",
  "publishedAt": "2026-08-29T02:30:01.000Z",
  "organizationId": "org_123",
  "resourceVersion": 4,
  "source": {
    "system": "marketplace",
    "marketplace": "shopee",
    "accountId": "1727079356",
    "providerEventId": "provider-event-123",
    "providerEventType": "order_status_push"
  },
  "subject": {
    "type": "order",
    "id": "3eb0da89-1cc7-45f9-a3e4-88124108ce04",
    "externalId": "260617BBKHHC7E",
    "externalNumber": "260617BBKHHC7E"
  },
  "data": {
    "order": {
      "id": "3eb0da89-1cc7-45f9-a3e4-88124108ce04",
      "marketplace": "shopee",
      "accountId": "1727079356",
      "externalOrderId": "260617BBKHHC7E",
      "externalOrderNumber": "260617BBKHHC7E",
      "status": "shipped",
      "marketplaceStatus": "SHIPPED",
      "country": "SG",
      "currency": "SGD",
      "totals": {
        "subtotal": { "amount": "140", "currency": "SGD" },
        "discount": { "amount": "0", "currency": "SGD" },
        "shipping": { "amount": "9.9", "currency": "SGD" },
        "tax": { "amount": "0", "currency": "SGD" },
        "total": { "amount": "149.9", "currency": "SGD" }
      },
      "itemCount": 2,
      "quantity": 3,
      "items": [
        {
          "id": "line_123",
          "productId": "prod_456",
          "externalOrderItemId": "marketplace-line-1",
          "marketplaceItemId": "marketplace-product-1",
          "marketplaceVariantId": "marketplace-variant-1",
          "sku": "LAMP-1-BLK",
          "sellerSku": "LAMP-1-BLK",
          "shopSku": null,
          "name": "Desk lamp - black",
          "status": "SHIPPED",
          "quantity": 3,
          "currency": "SGD",
          "unitPrice": { "amount": "49.9", "currency": "SGD" },
          "paidPrice": { "amount": "46.633333", "currency": "SGD" },
          "lineTotal": { "amount": "139.899999", "currency": "SGD" },
          "unitCost": { "amount": "18", "currency": "SGD" }
        }
      ],
      "placedAt": "2026-08-28T10:00:00.000Z",
      "sourceUpdatedAt": "2026-08-29T02:30:00.000Z",
      "createdAt": "2026-08-28T10:00:02.000Z",
      "updatedAt": "2026-08-29T02:30:00.000Z"
    },
    "previousStatus": "to_ship",
    "status": "shipped",
    "marketplacePreviousStatus": "READY_TO_SHIP",
    "marketplaceStatus": "SHIPPED",
    "changedFields": ["status", "marketplaceStatus"]
  }
}
```

## Example deletion

Deletion events contain stable identity only; they do not repeat a stale
financial snapshot.

```json theme={null}
{
  "schemaVersion": "2026-08-01",
  "id": "evt_28b01d752e4e4fc38be15f5f562fbbd2",
  "type": "order.deleted",
  "occurredAt": "2026-08-29T04:00:00.000Z",
  "publishedAt": "2026-08-29T04:00:00.000Z",
  "organizationId": "org_123",
  "resourceVersion": 5,
  "source": {
    "system": "marketplace",
    "marketplace": "shopify",
    "accountId": "example.myshopify.com",
    "providerEventId": "f9af83f7-6fb1-4d4f-bf55-50ae7d847f5b",
    "providerEventType": "orders/delete"
  },
  "subject": {
    "type": "order",
    "id": "3eb0da89-1cc7-45f9-a3e4-88124108ce04",
    "externalId": "gid://shopify/Order/123",
    "externalNumber": "#1042"
  },
  "data": {
    "order": {
      "id": "3eb0da89-1cc7-45f9-a3e4-88124108ce04",
      "marketplace": "shopify",
      "accountId": "example.myshopify.com",
      "externalOrderId": "gid://shopify/Order/123",
      "externalOrderNumber": "#1042",
      "deletedAt": "2026-08-29T04:00:00.000Z"
    }
  }
}
```

## Consumer rules

* Deduplicate by event `id`, not by provider event ID.
* Partition order state by `organizationId` and `subject.id`.
* Ignore an event when its `resourceVersion` is not newer than the last applied
  version for that subject.
* Replace the local integration view from `data.order`; do not merge raw
  marketplace payloads into the standardized resource.
* Treat unknown additive fields as forward-compatible and ignore them.
* Remove or tombstone the local order integration record on `order.deleted`.

The generated OpenAPI document contains reusable schemas and top-level webhook
operations for all four events. Download it from [openapi.json](/openapi.json).

See the [event catalog](/webhooks/event-catalog) for product, return,
settlement, and look resource families.
