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

# Webhooks

> Consume product, order, return, settlement, look, and review events from OmniCommerce.

OmniCommerce receives marketplace-specific pushes, refreshes the full resource,
and publishes one stable integration contract. Consumers do not need to
understand Shopee, Lazada, TikTok Shop, Shopify, Zalora, or Amazon payload
shapes.

> [!NOTE]
>
> Order events use the standardized `2026-08-01` envelope. Return and settlement
> events use standardized `2026-08-29` envelopes. Product, look, and review events use
> the original `eventId` / `eventType` envelope. Each event reference identifies
> its exact contract; do not assume fields from one envelope exist in another.

## Create a subscription

Create subscriptions with an organization API key or an OAuth token that has
the `webhooks:manage` scope. The endpoint must be a public HTTP(S) URL.

```bash theme={null}
curl -X POST "https://omnicommerce.sg/api/v1/webhooks" \
  -H "Authorization: Bearer $OMNI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.example.com/webhooks/omnicommerce",
    "description": "Commerce ingestion",
    "events": [
      "product.created",
      "product.updated",
      "product.deleted",
      "product.delisted",
      "product.price.changed",
      "product.inventory.changed",
      "order.created",
      "order.updated",
      "order.status.changed",
      "order.deleted",
      "return.created",
      "return.updated",
      "return.status.changed",
      "settlement.created",
      "settlement.updated",
      "settlement.status.changed",
      "look.created",
      "look.deleted",
      "review.created",
      "review.updated",
      "review.status.changed"
    ]
  }'
```

The `201` response contains the subscription and an `omni_whsec_...` signing
secret.

> [!WARNING]
>
> Store the signing secret when the subscription is created. It is returned only
> once. Do not put it in source control or application logs.

Manage subscriptions with the [Webhooks API](https://docs.omnicommerce.sg/api-reference.md). API-key requests
inherit their organization from the key and do not send `organizationId`.

See the [event catalog](https://docs.omnicommerce.sg/webhooks/event-catalog.md) for all accepted subscription
keys and event availability.

## Standardized envelope and versions

Every standardized order, return, or settlement event uses these top-level fields:

| Field | Meaning |
| - | - |
| `schemaVersion` | Wire-contract version; `2026-08-01` for orders or `2026-08-29` for returns/settlements |
| `id` | Stable event ID and recipient idempotency key |
| `type` | Canonical subscription event key |
| `occurredAt` | Time the canonical resource change occurred |
| `publishedAt` | Time the immutable event entered the delivery outbox |
| `organizationId` | OmniCommerce tenant boundary |
| `resourceVersion` | Increasing integer for one `subject.id` |
| `source` | Source system, marketplace, exact connected account, and provider receipt |
| `subject` | Stable internal and marketplace resource identity |
| `data` | Complete resource snapshot plus event-specific context |

Unknown additive fields must be ignored. Breaking changes receive a new
`schemaVersion`; existing versions remain stable during their support window.

Product and look events use `eventId`, `eventType`, `organizationId`, the
resource ID, `changedFields`, `occurredAt`, and `data`. Their reference pages
document the complete legacy envelope.

## Delivery guarantees

Events are written to the delivery outbox with their immutable resource
snapshot.

| Behavior | Contract |
| - | - |
| Acknowledgement | Any HTTP `2xx` response |
| Timeout | 10 seconds per delivery attempt |
| Retries | A failed delivery is retried up to three times |
| Idempotency | The event body and `X-Omni-Event-Id` remain stable across retries |
| Ordering | No global ordering; compare `resourceVersion` within one `subject.id` |
| Duplicates | At-least-once delivery means the same event can be received more than once |

Return any `2xx` response only after durably accepting the event.

## Verify signatures

Each POST includes:

```text theme={null}
X-Omni-Event-Id: evt_...
X-Omni-Event-Type: product.updated
X-Omni-Timestamp: 1780000000
X-Omni-Signature: <hex HMAC-SHA256>
```

Calculate HMAC-SHA256 over `{timestamp}.{rawBody}` with the subscription secret.
Compare the hexadecimal digest with `X-Omni-Signature` using a constant-time
comparison before parsing the JSON body.

```ts theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyOmniWebhook(input: {
  secret: string;
  timestamp: string;
  rawBody: string;
  signature: string;
}): boolean {
  const ageSeconds = Math.abs(Date.now() / 1000 - Number(input.timestamp));
  if (!Number.isFinite(ageSeconds) || ageSeconds > 300) return false;

  const expected = Buffer.from(
    createHmac("sha256", input.secret)
      .update(`${input.timestamp}.${input.rawBody}`)
      .digest("hex"),
    "hex",
  );
  const actual = Buffer.from(input.signature, "hex");
  return expected.length === actual.length && timingSafeEqual(expected, actual);
}
```

Keep the request body as raw bytes or text until verification succeeds. Parsing
and re-serializing JSON before verification changes the signed value.

## Process events safely

1. Read the raw request body.
2. Reject missing, stale, or invalid signature headers.
3. Deduplicate by `id`, legacy `eventId`, or `X-Omni-Event-Id` in durable storage.
4. For order, return, and settlement events, compare `resourceVersion` with the last
   applied version for `subject.id`.
5. Apply the complete snapshot in one local transaction.
6. Persist the event ID and, when present, resource version before returning
   `2xx`.

Buyer PII and delivery addresses are excluded from the standardized order
contract. Treat all remaining payload data as organization-confidential.

## Continue

> **[Event catalog](https://docs.omnicommerce.sg/webhooks/event-catalog.md)**
>
> Every accepted subscription event and current availability.

> **[Product webhooks](https://docs.omnicommerce.sg/webhooks/products.md)**
>
> Catalog lifecycle, price, and inventory events.

> **[Order webhooks](https://docs.omnicommerce.sg/webhooks/orders.md)**
>
> Event semantics, field definitions, and payload examples.

> **[Return webhooks](https://docs.omnicommerce.sg/webhooks/returns.md)**
>
> PII-free returned SKUs, refunds, and lifecycle changes.

> **[Settlement webhooks](https://docs.omnicommerce.sg/webhooks/settlements.md)**
>
> Marketplace fees, net settlement, and finance-status changes.

> **[Look webhooks](https://docs.omnicommerce.sg/webhooks/looks.md)**
>
> Shoppable-look creation and deletion events.

> **[Marketplace mapping](https://docs.omnicommerce.sg/webhooks/marketplace-mapping.md)**
>
> Provider topics, source identity, and normalization boundaries.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.