Skip to content

Receive a webhook

A signed event on your laptop, without deploying anything.

You do not need a public URL, a tunnel service, or a deployed environment to start. The CLI streams live sandbox events to your machine and signs them the same way production does, so the handler you write here is the handler you ship.

Write the endpoint

It has to do three things in this order: verify, acknowledge, then work.

# The request your endpoint will receive
POST /webhooks/wajub HTTP/1.1
X-Wajub-Signature: v1=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
X-Wajub-Timestamp: 1748083260
X-Wajub-Event: payment.succeeded
Content-Type: application/json

{"id":"evt_…","event":"payment.succeeded","data":{…}}

Point the CLI at it

Forward live sandbox events to localhost
npm install -g @wajub/cli
wajub login

wajub listen --forward-to localhost:3000/webhooks/wajub

The CLI prints a signing secret when it starts. Put that one in WAJUB_WEBHOOK_SECRET while you develop: forwarded events are signed with it, so your verification code runs for real rather than being skipped locally.

FlagWhat it does
--forward-toWhere to POST. http:// is assumed when you leave it off
--eventsComma-separated types, instead of everything
--secretUse a secret you already have, instead of a generated one

Make something happen

wajub trigger drives the sandbox API end to end and lets the real event come back to you. It is not a synthetic payload: a payment is actually created and processed.

Fire an event and watch it arrive
wajub trigger --list
wajub trigger payment.succeeded
wajub trigger payment.failed --amount 2500
wajub trigger customer.created -d name="Acme Inc"

Eight events can be triggered this way, and wajub trigger --list prints them.

ResourceEvents
Paymentspayment.created, payment.succeeded, payment.failed, payment.cancelled
Refundsrefund.succeeded, refund.failed
Customerscustomer.created, customer.updated

Anything outside that list you produce the normal way: create the resource with a sandbox test number and let it run.

Register the endpoint for real

The CLI covers development. Production needs a registered HTTPS endpoint, created in Konsole or through the API.

Create an endpoint
curl https://api.wajub.com/webhooks \
  -H "Authorization: sk.kZ3qP8mWvL2xR7tB5nY4hC6dF9jS1aG0eU3i…" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/webhooks/wajub",
    "events": ["payment.succeeded", "payment.failed", "refund.succeeded"]
  }'

The response carries a whsec_ secret, and that is the only time it is returned in full. Store it before you move on. Subscribe to the events you handle rather than to everything: an endpoint receiving types it ignores still has to answer them, and every one of them counts against your timeout budget.

Check the delivery, not just your logs

Konsole keeps every attempt with its response code and body. If your handler never ran, the answer is in Webhooks: a 404 means the URL is wrong, a timeout means you answered too late, and no row at all means the event never matched your subscription.

Webhook deliveries

last hour
MethodEndpointStatusDurationAttempt
POST/webhooks/wajub20086 ms1 of 5

What did you think of this content?