send0
Webhooks

Webhooks

Receive real-time notifications when email events occur.

Overview

Webhooks let you receive real-time HTTP POST notifications when events happen in your send0 account — like an email being delivered, opened, or bounced.

Instead of polling the API, send0 pushes events to your server as they happen.


How it works

  1. Register a webhook endpoint URL in your dashboard or via the API
  2. Subscribe to specific event types
  3. send0 sends a POST request to your URL when events occur
  4. Your endpoint responds with a 2xx status to acknowledge receipt

Webhook payload

Every webhook event has this structure:

{
  "id": "evt_3mKp7nQxLvRw",
  "object": "event",
  "type": "email.delivered",
  "created_at": "2026-04-12T10:35:22Z",
  "data": {
    "id": "em_2xKq9mNpLvRw",
    "to": "user@example.com",
    "subject": "Welcome!"
  }
}

Event types

EventTriggerData fields
email.queuedEmail acceptedid, to, subject
email.deliveredDelivered to inboxid, to, subject, delivered_at
email.openedRecipient openedid, to, subject, opened_at
email.clickedLink clickedid, to, subject, url, clicked_at
email.bouncedEmail bouncedid, to, bounce_type, reason
email.complainedMarked as spamid, to
email.failedDelivery failedid, to, error

Signature headers

Every webhook request includes these headers for verification:

X-Send0-Signature: t=1712921722,v1=abc123...
X-Send0-Webhook-ID: evt_xxxx
X-Send0-Timestamp: 1712921722

Handling webhooks

Express.js

import express from 'express';
import { Send0 } from 'send0';

const app = express();

app.post('/webhooks/send0', express.raw({ type: 'application/json' }), (req, res) => {
  const event = Send0.webhooks.verify(
    req.body,
    req.headers,
    process.env.SEND0_WEBHOOK_SECRET!,
  );

  switch (event.type) {
    case 'email.delivered':
      console.log(`Email ${event.data.id} delivered to ${event.data.to}`);
      break;
    case 'email.bounced':
      console.log(`Email ${event.data.id} bounced: ${event.data.reason}`);
      break;
    case 'email.complained':
      console.log(`Spam complaint from ${event.data.to}`);
      break;
  }

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

Important: Use express.raw() to get the raw request body. Parsed JSON will cause signature verification to fail.


Retry policy

send0 retries failed webhook deliveries up to 5 times with exponential backoff:

RetryDelay
1st30 seconds
2nd2 minutes
3rd15 minutes
4th1 hour
5th6 hours

A delivery is considered failed if your endpoint doesn't respond with 2xx within 30 seconds.


Best practices

  • Always verify webhook signatures before processing — see Webhook Verification
  • Respond with 200 quickly, then process asynchronously
  • Make your handler idempotent — you may receive the same event twice
  • Use the id field to deduplicate events
  • Log the X-Request-ID header for debugging