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 — except on a held link (data.link.settlement is "hold"), where they are held until you release or refund them.
payment.releasedA held payment was released: the seller was paid (minus your fee) and your fee was credited. data.hold carries the amounts.
payment.refundedA held payment was refunded to the payer. data.hold carries the amount and your reason.
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.