Handle duplicate events
Webhook endpoints might occasionally receive the same message more than once. A message is one event sent to one webhook. For example, if your endpoint processes a message but answers after our 16 second timeout, the attempt counts as failed and we send the message again. Make your event processing idempotent: log thewebhook-id of every message you’ve processed, and skip the ones you’ve already logged.
Use the webhook-id header alone as the idempotency key.
webhook-idis unique to each message, and it stays the same on every retry of that message.- If two of your webhooks subscribe to the same brand, each one receives its own message, with its own
webhook-id. webhook-delivery-idand the body’sdeliveryare new on every attempt. Don’t deduplicate on them.
Only listen to event types your integration requires
Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on your server and we don’t recommend it. You can change the events that a webhook endpoint receives by updating the webhook object using theupdateWebhook mutation.
Handle events asynchrounously
The volume of events that get generated can be spike-y. A data enrichment may result in millions of objects being updated in a very short period of time. Configure your handler to process incoming events with an asynchronous queue. You might encounter scalability issues if you choose to process events synchronously. Any large spike in webhook deliveries (for example, during a large data enrichment) might overwhelm your endpoint hosts. Asynchronous queues allow you to process the concurrent events at a rate your system can support. For example, your webhook endpoint could simply verify a payload and then push the event directly to a queue (like AWS SQS) where you can then process the events with better concurrency control.Receive events with an HTTPS server
You must use an HTTPS URL for your webhook endpoint. Brandfetch validates that the connection to your server is secure before sending your webhook data. For this to work, your server must be correctly configured to support HTTPS with a valid server certificate.Verify events are sent from Brandfetch
Brandfetch signs every delivery. Check the signature before you act on the body: it proves Brandfetch sent the request, and that nobody changed it on the way. Signatures follow version 1 of the Standard Webhooks specification. You don’t need to write the check yourself.Verify with an official library
Use the official Standard Webhooks libraries. Each one takes your webhook’s secret, the raw request body, and the request headers, and checks the signature and the timestamp. If either check fails, the library throws an error: answer400 and drop the delivery.
Your secret is the webhook’s secret field. createWebhook returns it, and the webhook and webhooks queries read it back. Pass it to the library exactly as shown, whsec_ prefix included.
Node.js
npm install standardwebhooks express. The sample is an ES module: save it as a .mjs file, or set "type": "module" in your package.json.
Verify without a library
If no official library fits your stack, implement the check exactly as the specification describes:- Read the
webhook-id,webhook-timestamp, andwebhook-signatureheaders. - Check that
webhook-timestamp, a Unix timestamp in seconds, is within 5 minutes of your clock. - Build the signed content: the
webhook-id, thewebhook-timestamp, and the raw body, joined with periods (.):{webhook-id}.{webhook-timestamp}.{body}. - Build the key: remove
whsec_from the start of your secret, and base64-decode the rest. - Compute the HMAC-SHA256 of the signed content with that key, and base64-encode it.
- Compare it with the signatures in
webhook-signature. The header holds one or more signatures separated by spaces, each writtenv1,<base64>. Accept the delivery if anyv1signature matches. Compare with a constant-time function, never with==.
Preventing replay attacks
A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. Thewebhook-timestamp header stops this. It holds the time of the attempt as a Unix timestamp in seconds, and it is part of the signed content, so an attacker can’t change it without invalidating the signature. Reject a delivery whose timestamp is more than 5 minutes from your clock. The official libraries do this for you. A replay inside those 5 minutes carries a webhook-id you’ve already processed, so deduplicating on it stops that too.
Use Network Time Protocol (NTP) to keep your server’s clock accurate. A clock that drifts past the tolerance rejects genuine deliveries.
Brandfetch generates the timestamp and signature each time we send an event to your endpoint. A retry gets a new timestamp and a new signature, so the retry of an event from days ago still passes the check.
Source IPs and allow-listing
Brandfetch sends every webhook delivery from one of these IP addresses:- At the perimeter, check the
Authorizationheader. Brandfetch sends the value you set withurlHeaderswith every delivery. Have your load balancer, API gateway, or WAF drop any request whoseAuthorizationheader is not exactly this value. A check that the header is present protects nothing. - In your application, verify the signature. The token keeps stray traffic away from your application. The signature proves Brandfetch sent the body, and that nobody changed it.
createWebhook or updateWebhook input. Use a long random token, such as the output of openssl rand -hex 32. The value must be printable ASCII, with no line breaks. The API never returns it once it’s set.
Authorization value. Brandfetch reads only the status code. A challenge page served with a non-2xx status fails the attempt, and the delivery is retried. One served with a 2xx status counts as delivered, and the event never reaches your application. An endpoint that fails for 14 days straight is switched off.