Skip to content
Listener.comDevelopers

Best practices

Build webhook handlers that are fast, safe and correct under retries.

1 min read

Respond fast, work later#

Return 2xx as soon as you have verified the signature and stored the event. Put the real work on a queue. A handler that calls other APIs before responding will eventually hit the 10-second timeout and trigger retries for events you already processed.

Verify every request#

Check the Listener-Signature on every request, against the raw body, with a timestamp tolerance. Reject anything that fails. See Verify signatures.

Handle duplicates#

Delivery is at least once. Store the id of each event you process (or use it as a unique key) and skip ids you have already handled. The same id is also in the Listener-Event-Id header, so you can deduplicate before parsing.

Do not rely on order#

Retries mean an older event can arrive after a newer one. Compare created timestamps, or treat events as a signal to re-read the current state with a GET. For example, on campaign.updated, fetch GET /v1/campaigns/{id} rather than applying previous_attributes blindly.

Subscribe to what you need#

List specific types in enabled_events instead of *. You get less traffic, and your handler is not surprised by new event types added later. If you do use *, ignore types you do not recognise and return 2xx for them.

Reconcile with the Events API#

Webhooks can be missed: your endpoint was disabled, a deploy dropped requests, a queue lost messages. Run a periodic job that lists recent events with created_gte and processes any you have not seen. That turns webhooks from “usually right” into “eventually always right”.

Keep secrets secret#

Store each endpoint’s signing secret in a secrets manager. Roll it with POST /v1/webhook-endpoints/{id}/roll-secret if it might have leaked; the previous secret stays valid for 24 hours while you deploy.

Use separate endpoints per environment#

Register one endpoint for production and another for staging, each with its own secret, so a staging bug never processes production events twice.