WebhooksEvent reference

Event reference

The event types Soxara emits today. This list is deliberately short — we only document events that actually fire, so you never subscribe to something that never arrives. More land here as they ship.

Payment events

EventWhen it fires
payment.completedA payment settled — a payment-link payment cleared, or a recurring charge succeeded. Funds are credited to your merchant wallet.
payment.failedA payment did not settle — card declined, MoMo timed out / cancelled, insufficient wallet balance, or an expired link. No funds moved.

(Internally, a link that expires also emits payment.failed — you reconcile on “did the money land?”, not the specific failure mode.)

These fire for every payment path: hosted-checkout link payments, sandbox simulate calls, and recurring mandate charges — all the same two events, same shape.

Payroll events

Fire once a payroll run reaches a terminal state — for a run created from the dashboard or via the API alike.

EventWhen it fires
payroll_run.completedEvery payment in the run succeeded.
payroll_run.partialSome payments succeeded, some didn’t (a bad phone match, insufficient wallet balance, etc.) — check data.payments for which.
payroll_run.failedNo payment in the run succeeded.
{
  "type": "payroll_run.completed",
  "createdAt": "2026-09-10T09:00:00Z",
  "data": {
    "run": { "id": "…", "merchant_id": "…", "status": "completed", "currency": "USD", "…": "…" },
    "payments": [
      { "external_ref": "emp-1029", "payee_label": "Adama Smith", "status": "completed", "amount": 150000, "…": "…" }
    ]
  }
}

Correlate on data.payments[].external_ref — the id you sent when creating each line.

Catalog events

EventWhen it fires
catalog.publishedA catalog version is published — fires once per publish with the full version snapshot, not per item edit. Draft edits never fire this.
{
  "type": "catalog.published",
  "createdAt": "2026-09-10T09:00:00Z",
  "data": {
    "catalog_id": "…",
    "version_id": "…",
    "published_at": "2026-09-10T09:00:00Z",
    "snapshot": { /* the same shape your public catalog read returns */ }
  }
}

Your integration can re-sync its whole catalog from data.snapshot on each delivery — there’s no partial/delta version to reconcile.

Inventory events

EventWhen it fires
inventory.low_stockAn item’s quantity crosses at or below its low-stock threshold. Fires once per crossing, not on every movement — recovering above the threshold does not fire a symmetric “restocked” event yet.
{
  "type": "inventory.low_stock",
  "createdAt": "2026-09-10T09:00:00Z",
  "data": {
    "item_id": "…",
    "name": "House Blend, 1kg",
    "sku": "HB-1KG",
    "current_qty": "4",
    "low_stock_threshold": "10",
    "unit": "bag"
  }
}

Treat this as a “go re-check this item” nudge, not a live, authoritative running total — poll the item’s own read endpoint if you need the current count.

Payload (payment events)

payment.completed/payment.failed share one shape (see Overview for the full field table):

{
  "type": "payment.completed",
  "createdAt": "2026-07-04T18:00:00Z",
  "data": {
    "payment": {
      "id": "…",
      "amount": 1200,
      "currency": "USD",
      "status": "completed",
      "payment_method": "card",
      "environment": "test",
      "mandate_id": "…",              // set on a recurring charge; null for a link payment
      "payment_link_id": null,        // set on a link payment; null for a charge
      "idempotency_key": "sub_42:2026-07",  // your key — the correlation handle
      "created_at": "…"
    },
    "link": { … }                     // a snapshot of the payment link, when the payment is link-bound
  }
}

Correlating an event back to your records

  • Recurring charges: match on data.payment.idempotency_key (the cycle key you sent on POST /v1/api/charges) or data.payment.mandate_id.
  • Payment links: match on data.link.id / data.link.link_code, or the idempotency key you set at link creation.

Coming later

Refunds, disputes, and richer lifecycle events will appear here when they’re actually emitted — not before. If your integration needs one that isn’t listed, tell us at [email protected] so we prioritise it.