Set up an endpoint
Register a webhook URL, choose its events, store the secret and send a test.
2 min read
You can manage endpoints in Settings → Developers → Webhooks in either app, or through the API with the webhooks:write scope. Both use the same endpoints, so anything you create in one shows up in the other.
1. Build a receiver#
Your endpoint must:
- Accept
POSTrequests with a JSON body over HTTPS. - Read the raw body (before JSON parsing) so you can verify the signature.
- Return a
2xxstatus within 10 seconds. Redirects are not followed and count as failures.
Do the slow work after responding: put the event on a queue and return 200 straight away.
2. Register it#
curl https://api.listener.com/v1/webhook-endpoints \
-H "Authorization: Bearer $LISTENER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/listener",
"enabled_events": ["campaign.created", "campaign.updated", "campaign.status_changed"],
"description": "Campaign sync"
}'enabled_eventsis a list of event types, or["*"]for every type, including types added later.- The response includes the signing
secret(whsec_…). Store it in your secrets manager. Lists and reads show only its last four characters;GET /v1/webhook-endpoints/{id}/secretreveals it again and needswebhooks:write, because the secret lets its holder forge deliveries. - A workspace can have up to 16 endpoints, disabled ones included.
URL rules#
URLs must use https://, carry no credentials or fragment, and must not resolve to private, loopback, link-local or other internal addresses. The check runs when you save the endpoint and again before every delivery. To receive webhooks on your laptop, expose your local server through a tunnel such as ngrok or cloudflared and register the tunnel’s HTTPS URL.
3. Send a test event#
curl -X POST https://api.listener.com/v1/webhook-endpoints/we_5Rk2LqP9xT1m/test \
-H "Authorization: Bearer $LISTENER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"event_type": "campaign.created"}'Listener sends a test event right away and returns the delivery, including your endpoint’s response status. Without event_type it sends webhook_endpoint.test; with one, it sends that type’s catalog example with data.test: true. Test deliveries are signed like real ones but are not retried and never count against the endpoint’s health. Use them to check signature verification end to end without changing any data.
Managing endpoints#
| Action | Request |
|---|---|
| List endpoints | GET /v1/webhook-endpoints |
| Change URL, events or description | PATCH /v1/webhook-endpoints/{id} |
| Pause or resume | PATCH /v1/webhook-endpoints/{id} with {"disabled": true} or false |
| Reveal the secret | GET /v1/webhook-endpoints/{id}/secret |
| Rotate the secret | POST /v1/webhook-endpoints/{id}/roll-secret |
| See recent deliveries | GET /v1/webhook-endpoints/{id}/deliveries |
| Redeliver one now | POST /v1/webhook-deliveries/{id}/retry |
| Delete | DELETE /v1/webhook-endpoints/{id} |
The Webhooks tab in the app shows the same delivery log with the request and response of each attempt.
Rotating the secret#
roll-secret returns a new secret. The previous secret keeps signing for 24 hours by default (expire_previous_in_hours, 0 to 168; the response says when in expires_previous_at), and during that time every delivery carries two v1 signatures, one per secret. Deploy the new secret, and verification keeps passing throughout. See Verify signatures.