โ AI Automation Mastery
Day 12 of 21
Webhook Architecture
Building a Reliable Webhook Receiver
Webhooks are the backbone of modern automation โ but receiving them reliably requires more than just exposing a URL. A production webhook receiver needs to handle high volume, verify authenticity, acknowledge quickly, and process asynchronously.
Fast acknowledgment is critical. When Stripe or GitHub sends a webhook, they expect a 200 response within 10 seconds. If your workflow takes 30 seconds to process (because it makes several API calls), the sender will time out, mark the webhook as failed, and retry. You'll process the same event 3 times.
The solution: split receiving from processing. Your webhook receiver immediately returns 200 and stores the raw payload in a queue (a database table or a queue service like Upstash). A separate workflow polls the queue, processes items, and deletes them when done. This pattern scales to thousands of webhooks per minute.
Webhook authentication is frequently overlooked. Most webhook senders include a signature header โ a hash of the payload signed with your secret key. Verify this signature before processing. Without verification, anyone who discovers your webhook URL can send fake events. In n8n, verify signatures in a Code node at the start of your webhook workflow.
Example signature verification for GitHub:
```javascript
const crypto = require('crypto');
const payload = JSON.stringify($input.first().json);
const signature = $input.first().headers['x-hub-signature-256'];
const expected = 'sha256=' + crypto.createHmac('sha256', 'YOUR_SECRET').update(payload).digest('hex');
if (signature !== expected) throw new Error('Invalid signature');
```
Sending Webhooks From Your Application
As you build automation products or integrate with partners, you'll need to send webhooks from your own application โ not just receive them. This requires thinking about reliability from the sender's perspective.
A naive webhook sender: when an event happens in your app, immediately make an HTTP request to the subscriber's URL. This is fragile โ what if the subscriber is down? What if the HTTP request times out? You've lost the event.
A production webhook sender: when an event happens, write it to a 'webhooks_pending' database table. A separate background job (or n8n scheduled workflow) polls this table, attempts delivery, records success or failure, and retries failed deliveries with exponential backoff (1 min, 5 min, 30 min, 2 hours, 24 hours). After 24 hours or 10 failed attempts, mark as permanently failed and notify the subscriber.
Webhook payload design matters. Always include: an event type ('payment.succeeded', 'user.created'), the object data relevant to the event, a unique event ID (allows subscribers to deduplicate), and a timestamp. Never send just an ID and expect the subscriber to look up the data โ network conditions may cause them to see stale data by the time they fetch it.
Document your webhooks well. Every external webhook should have documentation describing: what triggers it, the exact payload structure, how to verify the signature, and sample payloads for each event type. Undocumented webhooks create support burden.
โก Today's Action
Set up a webhook receiver in n8n with signature verification for one of your existing tools (Stripe, GitHub, or any webhook-capable SaaS). Test that it correctly rejects requests with invalid signatures.
๐ก Pro Tip
Add a webhook test mode โ a setting that lets you replay any past webhook event with the same payload. This is invaluable when a partner reports they received a malformed payload and you need to reproduce and debug the issue.