Skip to content

Webhooks

Endpoints, every delivery attempt, and how to replay one.

The Webhooks view has three tabs: the events your account produced, the deliveries made from them, and the endpoints they were sent to. Events have their own page; this one is about the other two.

Webhook deliveries

last hour
MethodEndpointStatusDurationAttempt
POST/webhooks/wajub50010021 ms1
POST/webhooks/wajub500240 ms2
POST/webhooks/wajub200142 ms3

Deliveries

One row per attempt, not per event. A delivery retried three times is three rows sharing one delivery id.

ColumnNote
Statussuccess, retrying or failed
Response codeWhat your endpoint answered, empty on a network error
DurationHow long your endpoint took. A row at 10,000 ms is a timeout
Attempt1 for the first try, then one more for each retry and each replay
EndpointWhich URL, when you have several

Filters: search over the event, status, endpoint, and a date range in whole days. Fifty per page.

The detail panel carries the payload that was sent and the body your endpoint returned, which is how you find out that your 500 came with a stack trace you can read from here.

Replaying

Two ways, both counted against your plan's Webhook replays per month allowance.

ActionScope
RetryOne delivery
Bulk retryUp to 100 failed deliveries at once, within a window, optionally for one endpoint

Bulk retry only picks up deliveries whose status is failed, inside the window you choose, which defaults to the last 24 hours. Filter the list on failed to see the button: with rows ticked it retries those, with none it asks for the window. Each delivery in the batch consumes one unit of the allowance, and the batch stops when the allowance runs out.

A delivery whose endpoint has since accepted the event is not sent again and costs nothing: a single retry says so, and a batch skips it. An event that failed several times goes out once.

Endpoints

Creating one takes a URL and a list of event types.

FieldRule
URLA valid URL, up to 2,048 characters, and not a private or internal address
EventsAt least one, each from the list Konsole offers
DescriptionFree text, worth using when you have several

Konsole checks the event names, the API does not

The picker only offers valid types and refuses anything else. POST /webhooks accepts any non-empty array, so a typo made through the API is stored and silently never matches. See the event catalogue.

Private and internal addresses are refused on purpose: an endpoint pointing at localhost or at a private range would let webhooks reach private networks. For local development, use wajub listen.

The secret

Each endpoint has its own signing secret, shown once at creation. Afterwards, Reveal secret shows it again, behind a re-confirmation of your password, and the action is recorded.

To rotate it, use Rotate next to the secret on the endpoint's page, behind the same password confirmation, or POST /webhooks/{id}/rotate-secret from the API. The new secret replaces the old one at once and is shown once. Every attempt sent from then on is signed with it, retries of events sent before the rotation included: until your handler knows it, it rejects them. Have the handler accept both secrets before you rotate, add the new one as soon as it is shown, then drop the old one. A delivery refused in between is retried, signed with the new secret. The overview's "Signing secret older than 6 months" card counts from the last rotation.

Moving an endpoint to live

Sandbox and live endpoints are separate. To receive live events, switch the Dashboard to live and create the endpoint again with the same URL and event list. It gets a new secret.

The page of a sandbox endpoint carries a Promote to Live button that does this copy in one action: it creates the live endpoint with the same URL and events, switches the Dashboard to live and shows the new live secret once. It refuses when a live endpoint already has that URL, or when the URL is not https://.

Send test sends a real delivery, with test data

Send test posts one delivery to the endpoint, shaped key for key like a real one and signed with the endpoint's real secret. It proves the URL is reachable, that your signature check passes, and that your handler accepts the envelope. Only its content is made up: the event is webhook.test and data is a placeholder, so it does not exercise the code behind a real event type.

What Send test posts
{
"id": "evt_test_kZ3qP8mWvL2xR7tB5nY4hC6d",
"event": "webhook.test",
"data": {
"id": "test_123",
"message": "This is a test webhook from Wajub."
},
"livemode": false,
"pending_webhooks": 1,
"api_version": "2026-10-11",
"request": {
"id": null,
"idempotency_key": null
},
"created": "2026-01-15T10:30:00+00:00"
}

It carries the X-Wajub-Delivery-Id header like any delivery, livemode follows the environment the Dashboard is in, and the attempt is recorded in the endpoint's deliveries as success or failed, from your answer. It is not retried, and it is not an event: GET /events does not list it.

Answer it with a 2xx

A handler that switches on event should treat an unknown type, webhook.test included, by answering 200 and doing nothing. To exercise the code behind a real type, use wajub trigger, which produces real events.

What did you think of this content?