Back to Blog
Engineering6 min read

Webhook Best Practices: Secure, Reliable Data Delivery

Webhooks are the backbone of real-time data delivery. When someone submits a SingleForm-powered form, we send that data to your endpoint instantly. But building a reliable webhook receiver takes thought. Here’s what we’ve learned.

1. Always Verify Signatures

Every SingleForm webhook includes an HMAC-SHA256 signature in the X-SingleForm-Signature header. This lets you verify the request actually came from us and wasn’t tampered with.

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Use timingSafeEqual to prevent timing attacks. Never use simple string comparison for signature verification.

2. Respond Quickly, Process Later

Your webhook should return a 200 response as fast as possible. If processing takes time (database writes, sending emails, calling APIs), queue the work and respond immediately.

app.post('/webhook', async (req, res) => {
  // Verify signature first
  if (!verifySignature(req.body, req.headers['x-singleform-signature'])) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Queue for async processing
  await queue.add('process-submission', req.body);

  // Respond immediately
  res.status(200).json({ received: true });
});

3. Handle Retries Gracefully

If your endpoint returns a 5xx error or times out, we’ll retry the delivery. This means your endpoint might receive the same submission multiple times.

Use the X-SingleForm-Delivery-Id header to deduplicate. Store processed delivery IDs and skip duplicates.

4. Use HTTPS

This should go without saying, but: always use HTTPS for your webhook endpoints. We won’t deliver to HTTP endpoints in production.

5. Return Meaningful Status Codes

Your response codes tell us what happened:

  • 200-299: Success. We mark the delivery as complete.
  • 400-499: Client error. We won’t retry (except 429).
  • 429: Rate limited. We’ll retry with backoff.
  • 500-599: Server error. We’ll retry.

6. Monitor Your Health Score

SingleForm tracks your webhook’s health score based on recent delivery success rates. If your score drops, we’ll alert you. Check the dashboard regularly to catch issues early.

Following these practices will help you build a webhook endpoint that’s secure, reliable, and ready for production traffic.