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

# Settlement webhooks

> Receive normalized marketplace fees, net settlement, and reconciliation changes independently from order lifecycle events.

Settlement webhooks are separate from `order.updated`. Marketplace finance can
arrive hours or days after an order, and providers can later adjust, reverse, or
void the same financial evidence.

OmniCommerce treats a Shopee, Lazada, TikTok Shop, Shopify, Zalora, or Amazon
push as a signal. It fetches the marketplace's authoritative finance detail,
normalizes it into one order-and-currency settlement aggregate, and emits a
developer webhook only when that committed aggregate changes.

## Events

| Event                       | Emitted when                                                                 |
| --------------------------- | ---------------------------------------------------------------------------- |
| `settlement.created`        | The first finance aggregate for one order and currency is committed          |
| `settlement.updated`        | Amounts, fees, references, evidence counts, or reconciliation results change |
| `settlement.status.changed` | Aggregate status changes, including pending, settled, reversed, or voided    |

Subscribe with `POST /api/v1/webhooks`. These keys can be combined with product,
order, return, and look events in the same subscription.

Use `GET /api/v1/settlements` with `settlements:read` for bootstrap and pull
reconciliation, and `GET /api/v1/settlements/{settlementId}` for one latest
snapshot. The pull API represents snapshots created by this event pipeline; run
the finance backfill before expecting older historical orders.

## Example

The Lazada finance panel shown in OmniCommerce is represented as normalized
money fields and an auditable fee breakdown:

```json theme={null}
{
  "schemaVersion": "2026-08-29",
  "id": "evt_01J...",
  "type": "settlement.status.changed",
  "occurredAt": "2026-08-29T04:15:00.000Z",
  "publishedAt": "2026-08-29T04:15:02.000Z",
  "organizationId": "org_123",
  "resourceVersion": 3,
  "source": {
    "system": "marketplace",
    "marketplace": "lazada",
    "accountId": "seller_456",
    "providerEventId": null,
    "providerEventType": "finance.synced"
  },
  "subject": {
    "type": "order_settlement",
    "id": "stl_92d31ec6d08a42f99a9af81e6f9a7ba4",
    "externalId": "1234567890",
    "externalNumber": "1234567890"
  },
  "data": {
    "previousStatus": "pending",
    "status": "settled",
    "changedFields": [
      "status",
      "marketplaceNet",
      "feeBreakdown",
      "evidenceCounts"
    ],
    "settlement": {
      "id": "stl_92d31ec6d08a42f99a9af81e6f9a7ba4",
      "orderId": "ord_123",
      "marketplace": "lazada",
      "accountId": "seller_456",
      "externalOrderId": "1234567890",
      "externalOrderNumber": "1234567890",
      "currency": "SGD",
      "status": "settled",
      "grossSales": { "amount": "17.88", "currency": "SGD" },
      "refunds": { "amount": "0", "currency": "SGD" },
      "subsidies": { "amount": "1.99", "currency": "SGD" },
      "platformFees": { "amount": "2.54", "currency": "SGD" },
      "fulfillmentFees": { "amount": "4.29", "currency": "SGD" },
      "operatingFees": { "amount": "0", "currency": "SGD" },
      "marketplaceNet": { "amount": "13.04", "currency": "SGD" },
      "calculatedNet": { "amount": "13.04", "currency": "SGD" },
      "variance": { "amount": "0", "currency": "SGD" },
      "paymentReceived": null,
      "reconciliationStatus": "reconciled",
      "reconciliationExceptionReason": null,
      "settlementReference": "LZD-PAYOUT-20260829",
      "payoutReference": null,
      "feeBreakdown": [
        {
          "feeType": "item_price_credit",
          "category": "gross_sales",
          "amount": { "amount": "17.88", "currency": "SGD" }
        },
        {
          "feeType": "shipping_fee_voucher_by_lazada",
          "category": "subsidies",
          "amount": { "amount": "1.99", "currency": "SGD" }
        },
        {
          "feeType": "commission",
          "category": "platform_fees",
          "amount": { "amount": "1.95", "currency": "SGD" }
        },
        {
          "feeType": "payment_fee",
          "category": "platform_fees",
          "amount": { "amount": "0.59", "currency": "SGD" }
        },
        {
          "feeType": "shipping_fee_paid_by_seller",
          "category": "fulfillment",
          "amount": { "amount": "4.29", "currency": "SGD" }
        }
      ],
      "evidenceCounts": {
        "estimated": 0,
        "pending": 0,
        "settled": 5,
        "reversed": 0,
        "voided": 0
      },
      "sourceOccurredAt": "2026-08-29T04:15:00.000Z",
      "updatedAt": "2026-08-29T04:15:00.000Z"
    }
  }
}
```

All money values are decimal strings paired with an ISO 4217 currency. Never
sum settlement resources across currencies without an explicit FX conversion.

## Status model

| Status              | Meaning                                                         |
| ------------------- | --------------------------------------------------------------- |
| `estimated`         | Only estimated finance evidence is available                    |
| `pending`           | Marketplace finance exists but is not final                     |
| `partially_settled` | Some evidence settled while other effective evidence is pending |
| `settled`           | All effective evidence is settled                               |
| `reversed`          | No effective evidence remains and a prior observation reversed  |
| `voided`            | No effective evidence remains and observations were voided      |

`paymentReceived`, when present, is a separate formula-backed unit-economics
value with `formulaId` and `formulaVersion`. It is not the seller settlement
net and should not replace `marketplaceNet`.

## Processing guidance

Use `id` as the delivery idempotency key. Track the highest `resourceVersion`
for each `subject.id`, and ignore older versions that arrive out of order. Apply
the complete `data.settlement` snapshot rather than incrementally replaying
`changedFields`.

If a provider push arrives before finance is ready, OmniCommerce does not expose
the provider's partial body. A later finance refresh creates or updates the
normalized settlement resource. Duplicate source upserts that do not change the
resource produce no new public event.
