Skip to main content

Testing locally

You can test your webhook handler on your own machine using a sandbox endpoint.

You don't need a mock server or fixture files. You can use real event payloads, real signatures, and real retries.

Sandbox is a little more relaxed than production: plain http:// endpoints are allowed in sandbox. Production requires HTTPS. Everything else works the same way.

Getting your handler a URL​

Pick the setup that matches how you're working.

Running the API locally. The dispatcher runs in the same process, so it can reach your laptop directly. Register http://localhost:3000/webhooks and you're ready to go.

Using the hosted sandbox. Deliveries come from our servers, so they need a hostname they can reach. A tunnel is the easiest option:

# ngrok
ngrok http 3000

# or cloudflared
cloudflared tunnel --url http://localhost:3000

Both commands give you a public URL. Register that URL as your webhook endpoint.

Free tunnel URLs usually change when you restart them. Instead of changing the endpoint every time, you can point the tunnel at a small local proxy, or keep a second endpoint registered and switch between them.

Registering the endpoint​

Webhook endpoints are managed in Cockpit under Developers → Webhooks. There is no API for creating them.

You'll need to set three things:

  • URL — your tunnel URL or localhost, including the handler path.
  • Events — use * while developing. You can narrow this down later.
  • Description — give it a name that tells you which machine it's for.

Save the endpoint, then copy its signing secret:

export ALETA_WEBHOOK_SECRET=whsec_...

Your handler needs this secret to verify webhook signatures. Signature verification can't be turned off.

Testing webhook event​

Click Send test event on the endpoint.

This sends a test delivery directly to that endpoint, so it doesn't matter which events the endpoint is subscribed to.

The button only tells you that the event was queued. The dispatcher should pick it up within a second or so, and the result will appear in the delivery log.

The event looks like this:

{
"id": "evt_01M480J2CTN4T8D9E8D2TBN1KC",
"object": "event",
"type": "webhook.test",
"api_version": "2026-08-01",
"environment": "sandbox",
"created_at": "2026-10-06T07:06:30Z",
"data": {
"object": {
"object": "webhook_test",
"endpoint_id": "whe_01HX8ZQ2K4M7P9RSTVWXYZAB",
"message": "This is a test event from AletaPlanet Cockpit.",
"sent_at": "2026-10-06T07:06:30Z"
}
}
}

A test event has a real signature, so it's useful for testing your signature verification.

One thing to remember: you can choose any event type for a test, but data.object is always a webhook_test object.

For example, if you send a test as card.frozen, data.object.id won't exist. Use webhook.test to check connectivity and signature verification. Use real events to test event-specific handlers.

Debugging from the delivery log​

Each delivery attempt records what happened:

  • HTTP status
  • first 8 KB of the response body
  • request duration
  • transport error, if the request didn't complete

Check the response body when debugging. If your framework returns a stack trace with a 500, you'll usually find it right there in the delivery log.

Before you go to production​

Create a separate production endpoint. Endpoints and signing secrets don't carry over between environments.

Check these things first:

  • The production URL uses HTTPS and has a valid certificate. http:// only works in sandbox.
  • Your handler returns 2xx within 10 seconds. If it needs to do real work, put that work behind a queue.
  • Your handler returns 2xx for event types it doesn't recognise. A 4xx causes retries and counts toward the 20 consecutive failures that disable an endpoint.
  • Deduplication uses event.id. Test it by replaying a delivery.

Keep the sandbox endpoint registered. It doesn't cost anything, and it'll be useful the next time you change your handler.

Common issues​

SymptomCause
Delivery log shows a transport error, but your handler gets nothingThe tunnel is down, or the endpoint still points to an old tunnel URL
signature does not match on every deliveryJSON middleware changed the body before you verified it
Signature fails only for an old saved payloadThe 5-minute timestamp window has expired. Sign it again
Test events arrive, but real events don'tThe endpoint isn't subscribed to those events. Test events are sent directly to the endpoint
data.object.id is undefined on a test eventTest events always contain a webhook_test object
Endpoint stopped receiving events20 consecutive failures disabled it. Re-enable it and reconcile the missing period through the API
Handler runs twice for one actionTwo endpoints point to the same service, or the delivery was retried

Next​