For more details on why you should store your webhook data, see the data
storage guide.
Why use the Webhook Ingester?
While you can build your own webhook handler, the Webhook Ingester provides a production-ready solution with:- Signature verification using Svix to ensure webhook authenticity
- Idempotent storage that safely handles duplicate webhook deliveries
- Multiple database support including PostgreSQL, MySQL, MongoDB, and data warehouses
- One-click deployment to Vercel, Railway, or Render
Deploy
Get started in minutes with one-click deployment:Supported Databases
| Database | Endpoint | Best For |
|---|---|---|
| Supabase | /supabase | Quick setup with managed Postgres |
| PostgreSQL | /postgresql | Self-hosted or managed Postgres (Neon, Railway, Render) |
| MySQL | /mysql | Self-hosted or managed MySQL |
| PlanetScale | /planetscale | Serverless MySQL |
| MongoDB | /mongodb | Document database (Atlas, self-hosted) |
| Snowflake | /snowflake | Data warehousing and analytics |
| BigQuery | /bigquery | Google Cloud analytics |
| ClickHouse | /clickhouse | High-performance analytics |
Quick Start
1
Clone and install
2
Configure environment variables
Copy the example environment file and add your credentials:At minimum, you need:
.env.local
Get your webhook signing secret from the Resend
Dashboard when creating a webhook.
3
Set up your database
Set up your database and run the provided schema for your database. The ingester supports PostgreSQL, MySQL, MongoDB, and several data warehouses. Schema files can be found in the
schemas/ directory.4
Deploy and register webhook
Deploy to your preferred platform, then register your webhook endpoint in the Resend Dashboard and select all the events you’d like to store.Your endpoint URL will be:
https://your-domain.com/{connector}For example: https://your-app.vercel.app/postgresqlDatabase Schemas
The ingester creates three tables to store webhook events:| Table | Description |
|---|---|
resend_wh_emails | All email events (sent, delivered, bounced, opened, clicked, etc.) |
resend_wh_contacts | Contact events (created, updated, deleted) |
resend_wh_domains | Domain events (created, updated, deleted) |
svix_id- Unique webhook event ID for idempotencyevent_type- The type of event (e.g.,email.delivered)event_created_at- When the event occurredwebhook_received_at- When the webhook was received- Event-specific fields (email details, bounce info, click data, etc.)
Idempotency
The ingester handles duplicate webhooks automatically. Each webhook includes a uniquesvix-id header, and the ingester uses this to ensure events are stored only once.
If Resend retries a webhook delivery (due to a temporary failure), the duplicate will be safely ignored without creating duplicate records in your database.
Configuration Reference
Required Environment Variables
| Variable | Description |
|---|---|
RESEND_WEBHOOK_SECRET | Your webhook signing secret from Resend |
Database-Specific Variables
Supabase
Supabase
PostgreSQL
PostgreSQL
MySQL
MySQL
PlanetScale
PlanetScale
MongoDB
MongoDB
Snowflake
Snowflake
BigQuery
BigQuery
ClickHouse
ClickHouse
Example Queries
Once your data is stored, you can run analytics queries. Here’s an example to get email status counts by day:See the
queries_examples.md
file in the repository for more analytics queries including bounce rates, open
rates, and click-through rates.
Data Retention
By default, webhook events are stored indefinitely. To implement data retention policies, you can set up scheduled jobs to delete old events. Example for PostgreSQL (delete events older than 90 days):Troubleshooting
Webhook signature verification failing
Webhook signature verification failing
- Ensure
RESEND_WEBHOOK_SECRETmatches the signing secret in your Resend Dashboard - Make sure you’re using the raw request body for verification - Check that the secret hasn’t been rotated in Resend
Database connection errors
Database connection errors
- Verify your database credentials are correct - Check that the schema has been applied to your database - Ensure your database is accessible from your deployment (check firewall rules)
Webhooks not being received
Webhooks not being received
- Verify your endpoint URL is publicly accessible - Check the webhook status in your Resend Dashboard - Ensure your server responds with HTTP 200 for successful requests