Developers
Webhooks and events
Signed webhook deliveries: registering an endpoint, verifying the x-wc-signature header, the retry schedule, and every event type Soca emits with what to do when it fires.
Webhooks are how Soca tells your backend that money moved. Register an HTTPS endpoint in Dashboard → Developers → Webhooks and you receive a signing secret starting with whsec_. From then on, every event is POSTed to your endpoint as JSON:
x-wc-signature: t=<unix-seconds>,v1=<hex hmac>: the signature to verify (below).wc-event-type: <event type>: the event type, duplicated as a header for cheap routing.
Verifying signatures
The signature is HMAC-SHA256 over "<t>.<raw request body>" with your whsec_ secret, compared timing-safe against v1. Reject deliveries whose t is more than 5 minutes old; that is the replay window. Always verify against the raw body, before any JSON parsing or re-serialization.
With the SDK it is one call:
Without the SDK, the same check in plain Node:
Retries
Respond with any 2xx status to acknowledge a delivery. On a non-2xx response or a network error, Soca retries with backoff. The next attempts come 1, 5, 30, and 120 minutes after each failure, for 5 attempts in total. The delivery is then marked failed and recorded for inspection. Deliveries can arrive late or, rarely, more than once, so key your fulfillment on the event id and make duplicates a no-op.
Event types
Payments and subscriptions emit exactly these events:
| Event | When it fires | What to do |
|---|---|---|
invoice.paid | A one-time invoice was paid. | Fulfill the order. This is the fulfillment signal for one-time payments. |
refund.succeeded | A refund completed on-chain. | Reverse the fulfillment or record the credit in your books. |
subscription.active | The first successful subscription charge. | Start access. This is the fulfillment signal for subscriptions. |
subscription.renewed | A renewal charge succeeded. | Extend access for another period; usually nothing else. |
subscription.payment_failed | A renewal charge failed. Dunning (automatic retries) begins. | Optionally notify the customer to top up their wallet. Do not revoke access yet. |
subscription.dunning_exhausted | All renewal retries failed; the subscription is lost. | End access and treat the subscription as churned. |
subscription.revoked | The customer revoked their on-chain authorization. | End access immediately; no further charges are possible. |
subscription.expired | The subscription reached its scheduled end. | End access. |
Usage-billing actions taken through the API append to the same event feed and ride the same signed, retried delivery path:
| Event | When it fires |
|---|---|
credit.granted | A credit grant was applied via POST /v1/credit_grants. |
credit.debited | Credits were drawn down via POST /v1/credit_debits. |
entitlement.granted | A feature was granted via POST /v1/entitlement_grants. |
There is no payment.confirmed event
Do not wait for one. Fulfill one-time purchases on invoice.paid and start subscription access on subscription.active; revoke on subscription.revoked, subscription.dunning_exhausted, or subscription.expired.
Reconciliation
The same events are queryable at `GET /v1/events`. If your endpoint was down past the retry window, page through the feed and process anything you missed. Webhooks are the push; the events list is the pull.
Was this page helpful?
A quick signal helps us improve the right pages.