Webhooks

Best Practices

A checklist for a secure, reliable webhook receiver, covering verification, de-duplication, ordering, quick acknowledgement and secret handling.

Security

  • Verify every signature against the raw body, in constant time, and reject timestamps more than 5 minutes from your clock. See Verifying Signatures.
  • Keep the signing secret secret. Store it in a secret manager, never in source code. It is shown only once; if it is lost or exposed, rotate it.
  • Use HTTPS with a valid certificate. Endpoints must be https:// on port 443 or 8443, on a public host name.
  • Treat the receiver URL as sensitive. Do not put credentials in it, and do not reuse it for anything else. Rely on the signature, not on the URL being secret.
  • Do not rely on the source address. Deliveries are not sent from a fixed, published set of addresses, so an IP allowlist is not a substitute for verifying signatures.
  • Treat the payload as a notification, not as authority. For anything consequential, fetch the current record through the API with a token you control before acting.
  • Choose the owner deliberately. Payloads follow the owner's visibility, and the endpoint stops when the owner leaves or is no longer an Administrator or Executive. Give it an owner whose access matches what the receiving system should see.

Reliability

  • Acknowledge quickly. Verify, store the event, return 2xx, then process it in the background. Deliveries time out after 10 seconds.
  • De-duplicate on the event id. Delivery is at least once; the same event can arrive more than once, including after a redelivery. Record processed ids and skip repeats.
  • Do not assume order. Use created_at to sort events, and refetch the record from the API when the latest state matters.
  • Ignore unknown event types and unknown fields, so new additions do not break your receiver.
  • Handle thin events. If data.object only contains id and object, fetch the full record through the API.
  • Watch the delivery log. An endpoint whose deliveries keep failing is turned off after 72 hours, and resuming it does not replay what was missed.

Testing

  • Use Send test event after creating an endpoint, and after every deployment of your receiver.
  • Check that your receiver rejects a request with a changed body, a wrong signature and an old timestamp, not only that it accepts a genuine one. The test vector checks the signature computation itself.
  • Make your receiver fail once on purpose, and confirm the next retry succeeds and is processed once.

Receiver checklist

CheckDone when
Raw body available before JSON parsingSignature verification uses the exact bytes received
Signature verified in constant timeA tampered body is rejected
Timestamp tolerance enforcedA delivery signed more than 5 minutes ago is rejected
Multiple signatures acceptedRotating the secret causes no failed deliveries
De-duplication on idA repeated event is processed once
Fast acknowledgementResponds in well under 10 seconds
Unknown types ignoredA new event type returns 2xx and is skipped
Thin events handledAn event with only id and object is fetched from the API