Skip to main content
This page walks through configuring Soap to send you webhooks and building an endpoint that handles them correctly. For the event catalog and payload shapes, see Webhooks Overview.
1

Set your webhook URL and copy your signing secret

In your Soap Dashboard, go to Developers and set the URL where Soap should send webhook events. Then copy your webhook signing secret (format wss_...) — you’ll need it to verify signatures.
Treat your signing secret like a password. Store it in an environment variable, never commit it to source control, and never send it to the client.
2

Enable the events you need

4 events are always sent and require no action: checkout.succeeded, checkout.hold, checkout.release_hold, and checkout.returned.The other 8 events are off by default and must be explicitly enabled in Dashboard → Developers before Soap will ever deliver them:
  • checkout.pending
  • checkout.failed
  • checkout.voided
  • checkout.expired
  • checkout.terminally_failed
  • checkout.review.created
  • checkout.review.approved
  • checkout.review.declined
If you write a handler for one of these events and it never seems to fire, check whether it’s enabled here first.
3

Expose localhost for local development

Soap’s servers can’t reach localhost directly. Use a tunneling tool such as ngrok to expose your local dev server:
Point your dashboard webhook URL at the resulting https://*.ngrok.io (or ngrok-free.app) URL plus your webhook path, e.g. https://abcd1234.ngrok.io/webhooks/soap. Update it again if the tunnel URL changes.
4

Build an endpoint that reads the raw body, verifies, and responds quickly

Your endpoint must:
  1. Read the raw request body — not a body that’s already been parsed into an object and would need to be re-serialized. See the raw body warning in the Webhooks Overview.
  2. Verify the SOAP-WEBHOOK-SIGNATURE header against that raw body.
  3. Return a 2xx status quickly — before doing slow work like calling other APIs or waiting on a database transaction.
  4. Do any slow processing asynchronously, after responding, e.g. by enqueueing a background job.
Delivery attempts time out after 10 seconds. If your handler is slow, Soap sees it as a failure and retries — which can cause duplicate processing if you’re not using event_id as an idempotency key.
5

Handle checkout.hold specially

checkout.hold is the one event where your response status changes what Soap does next:
  • Respond 2xx to indicate the customer has sufficient available funds — the withdrawal proceeds.
  • Respond non-2xx to indicate the customer has insufficient funds — the withdrawal does not proceed.
Unlike every other event, checkout.hold is delivered synchronously and is not retried, so this check needs to complete within the 10 second timeout. See checkout.hold for details.

Minimal Express Handler

Testing Your Handler

There is currently no “send test webhook” button in the dashboard. To exercise your handler end-to-end, complete a real checkout against the sandbox:
  1. Point your webhook URL at your local tunnel (see above).
  2. Create a sandbox checkout and complete it with the test card 4111-1111-1111-1111 and CVV 999 (see Testing for other sandbox test methods).
  3. Watch your endpoint receive the resulting checkout.succeeded (and, for withdrawals, checkout.hold) delivery.
To see the other 8 events, remember to enable them first (Step 2 above) and drive the corresponding scenario — e.g. a declined test card for checkout.failed.