Open reference notes for SMB AI implementation — voice agents, RAG chatbots, n8n workflows. Maintained by https://pxlpeak.com
Most n8n tutorials ship public webhooks with zero auth. That’s fine for personal automation, dangerous for client work. This is what we use at PxlPeak in production.
An n8n Webhook node exposes a URL like https://n8n.example.com/webhook/abc123. Out of the box:
For a Shopify order-processing workflow, a misused webhook can create duplicate orders, trigger repeat charges, or spam your fulfillment queue. This has happened to clients who came to us after the incident.
If the webhook source is a known platform (Shopify, Twilio, Stripe, Vapi), always verify the signature the platform sends.
Shopify example (in an n8n Code node directly after the Webhook trigger):
const crypto = require('crypto');
const secret = $env.SHOPIFY_WEBHOOK_SECRET;
const hmacHeader = $input.first().json.headers['x-shopify-hmac-sha256'];
const rawBody = $input.first().json.rawBody; // configure Webhook node to pass raw body
const digest = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('base64');
if (digest !== hmacHeader) {
throw new Error('Invalid Shopify webhook signature');
}
return $input.all();
Twilio, Stripe, Vapi all have equivalents. Use them. Do not skip this because “it’s just internal.”
For internal-to-internal webhooks (your app calling n8n), use a shared secret in a header. Reject missing or wrong headers before any real work happens.
const expected = $env.INTERNAL_WEBHOOK_SECRET;
const provided = $input.first().json.headers['x-internal-secret'];
if (!provided || !crypto.timingSafeEqual(
Buffer.from(provided),
Buffer.from(expected)
)) {
throw new Error('Unauthorized');
}
Use timingSafeEqual — a naive === is vulnerable to timing attacks. The difference is microseconds but the exploit is real.
If the source is a fixed platform with a published IP range (Shopify, Twilio), put an IP allowlist at the reverse proxy (Nginx, Cloudflare) layer before n8n.
Do not rely on n8n alone for this. Defense in depth.
A compromised token can still hammer your webhook. Rate-limit at the proxy. For SMB clients a reasonable default is 60 requests/minute per source IP; higher-volume clients need tuning.
Before running any business logic, validate the payload has the expected shape.
const required = ['order_id', 'customer_email', 'line_items'];
const body = $input.first().json;
for (const field of required) {
if (!(field in body)) {
throw new Error(`Missing required field: ${field}`);
}
}
If the payload is malformed, fail fast. Don’t half-process.
Every webhook handler we write assumes the same event may arrive twice. Use the event ID (Shopify: order.id, Twilio: MessageSid) as an idempotency key. Store seen IDs in Redis or Postgres for 24h minimum.
const eventId = $input.first().json.order_id;
// Check Redis for recent occurrence
const seen = await redis.get(`webhook:shopify:${eventId}`);
if (seen) {
return { status: 'already_processed', event_id: eventId };
}
await redis.set(`webhook:shopify:${eventId}`, '1', 'EX', 86400);
// proceed with real work
This is the single most common production-n8n bug we fix at PxlPeak — clients whose agency set up webhooks without idempotency, then got duplicate orders during a Shopify retry burst.
On errors, never echo back the internal state. Return a generic 500 and log the detail to your observability system.
try {
// ...
} catch (err) {
console.error(`[webhook-error] ${err.message}`, { event_id: eventId });
return { status: 'error' };
}
An error message like PostgreSQL: password authentication failed for user "n8n_prod" at host "internal-db.example.com" handed to a random caller is a handoff of your infrastructure.
Never hardcode a secret inside a workflow node. Use n8n’s environment variable mechanism or, better, a secrets manager (Doppler, 1Password, AWS Secrets Manager). Rotate quarterly.
Ask for every new webhook:
Most “just get it working” implementations skip these. Four of them are one-liners to add. Add all eight.
Need production-grade n8n built for you? PxlPeak deploys automation workflows for SMB service businesses in 5 business days. Client owns the n8n instance and all credentials. Zero vendor lock-in. Book a 15-min intro.