Overview
Webhooks allow you to receive HTTP POST requests to your server when events occur in useSend, such as when an email is delivered, bounced, or clicked. This enables you to build real-time integrations and automate workflows.Setting up webhooks
1
Create a webhook endpoint
Create an endpoint on your server that can receive POST requests. The endpoint must:
- Accept POST requests with JSON body
- Return a 2xx status code to acknowledge receipt
- Respond within 10 seconds
2
Add webhook in dashboard
Go to Webhooks in your useSend dashboard and create a new webhook:
- Enter your endpoint URL
- Select which events you want to receive
- Copy the signing secret for verification
3
Verify webhook signatures
Always verify webhook signatures to ensure requests are from useSend. See the Signature Verification section below.
Event types
Email events
| Event | Description |
|---|---|
email.queued | Email has been queued for sending |
email.sent | Email has been sent to the recipient’s mail server |
email.delivered | Email was successfully delivered |
email.delivery_delayed | Email delivery is being retried |
email.bounced | Email bounced (permanent or temporary) |
email.rejected | Email was rejected |
email.complained | Recipient marked email as spam |
email.failed | Email failed to send |
email.cancelled | Scheduled email was cancelled |
email.suppressed | Email was suppressed (recipient on suppression list) |
email.opened | Recipient opened the email |
email.clicked | Recipient clicked a link in the email |
Contact events
| Event | Description |
|---|---|
contact.created | New contact was created |
contact.updated | Contact was updated |
contact.deleted | Contact was deleted |
Domain events
| Event | Description |
|---|---|
domain.created | New domain was added |
domain.verified | Domain verification completed |
domain.updated | Domain settings were updated |
domain.deleted | Domain was deleted |
Webhook payload
Each webhook request includes a JSON payload with the following structure. See Event data details for details on thedata field for each event type.
Payload fields
| Field | Description |
|---|---|
id | Unique identifier for this webhook call |
type | The event type (e.g., email.delivered) |
version | API version for the payload format |
createdAt | When the event was created |
teamId | Your team ID |
data | Event-specific data (varies by event type) |
attempt | Delivery attempt number (1-6) |
Request headers
Each webhook request includes the following headers:| Header | Description |
|---|---|
X-UseSend-Signature | HMAC-SHA256 signature for verification |
X-UseSend-Timestamp | Unix timestamp in milliseconds |
X-UseSend-Event | Event type |
X-UseSend-Call | Unique webhook call ID |
X-UseSend-Retry | true if this is a retry attempt |
Signature verification
Always verify webhook signatures to ensure requests are authentic. The signature is computed as:Using the SDK (Recommended)
Next.js App Router
Express
Verification only
If you only need to verify the signature without parsing:Manual verification
If you prefer to verify manually without the SDK:Retry behavior
If your endpoint doesn’t return a 2xx response, useSend will retry delivery with exponential backoff:| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | ~5 seconds |
| 3 | ~10 seconds |
| 4 | ~20 seconds |
| 5 | ~40 seconds |
| 6 | ~80 seconds |
Best practices
Respond quickly
Respond quickly
Return a 2xx response as soon as possible. Process webhook data
asynchronously if needed. Requests timeout after 10 seconds.
Handle duplicates
Handle duplicates
Use the
id field in the payload to deduplicate events. In rare cases, the
same event may be delivered more than once.Verify signatures
Verify signatures
Always verify the
X-UseSend-Signature header to ensure requests are from
useSend and haven’t been tampered with.Check timestamps
Check timestamps
The SDK rejects signatures older than 5 minutes by default. This prevents
replay attacks.
Use HTTPS
Use HTTPS
Always use HTTPS endpoints in production to encrypt webhook data in transit.
Testing webhooks
You can send a test webhook from the dashboard to verify your endpoint is working correctly:- Go to Webhooks
- Click on your webhook
- Click “Send Test” to send a test event
webhook.test with the following payload:
Troubleshooting
Webhook not receiving events
Webhook not receiving events
- Verify your endpoint URL is correct and publicly accessible - Check that your endpoint returns a 2xx status code - Ensure the webhook is set to ACTIVE status in the dashboard - Check if the webhook was auto-disabled due to consecutive failures
Signature verification failing
Signature verification failing
- Use the raw request body, not parsed JSON - Ensure you’re using the
correct webhook secret - Check that the timestamp hasn’t expired (5 minute
window) - Verify you’re computing the HMAC correctly:
HMAC-SHA256(secret, "${timestamp}.${rawBody}")
Webhook auto-disabled
Webhook auto-disabled
After 30 consecutive failures, webhooks are automatically disabled. Fix the
issue with your endpoint, then re-enable the webhook from the dashboard. The
failure counter resets on the next successful delivery.
Event data details
This section documents thedata field structure for each event type.
Email events
Most email events share a common base structure:email.bounced
Includes additional bounce details:email.failed
Includes failure reason:email.suppressed
Includes suppression details:email.opened
Includes open tracking details:email.clicked
Includes click tracking details:Contact events
All contact events (contact.created, contact.updated, contact.deleted) include:
Domain events
All domain events (domain.created, domain.verified, domain.updated, domain.deleted) include: