Appearance
Webhooks API
Receive real-time delivery status notifications via HTTP webhooks.
Endpoints
POST /v1/webhooks— create webhook endpointGET /v1/webhooks— get webhook endpointPATCH /v1/webhooks/{uuid}— update webhook endpointDELETE /v1/webhooks/{uuid}— delete webhook endpoint
One webhook endpoint per application. All endpoints require authentication.
Event Types
| Event | When | Channels |
|---|---|---|
delivery.sent | Provider accepted the message | All |
delivery.delivered | Delivery confirmed by provider | Email (SES confirmation) |
delivery.failed | Send failed or provider rejected | All |
delivery.bounced | Hard bounce (contact suppressed) | |
delivery.complained | Spam complaint (contact suppressed) |
delivery.delivered
delivery.delivered is only emitted when we receive actual delivery confirmation from the provider. For channels without delivery receipts (Slack, Discord, Push), you'll receive delivery.sent only.
Webhook Payload
Every webhook POST contains a JSON payload:
json
{
"event": "delivery.sent",
"delivery": {
"uuid": "d3f1a2b4-5678-9012-3456-789012345678",
"channel": "email",
"status": "sent",
"event_name": "welcome",
"message_id": "provider-message-id",
"sent_at": "2026-03-30T12:00:00.000Z",
"delivered_at": null,
"error": null
},
"request_id": "r8e5c1a2-1234-5678-9012-345678901234",
"timestamp": "2026-03-30T12:00:00.123Z"
}For failed deliveries, error contains structured details:
json
{
"error": {
"code": "HARD_BOUNCE",
"message": "Permanent bounce: mailbox does not exist"
}
}Correlate webhook events with your original send request using delivery.uuid or request_id.
Signature Verification
Every webhook includes an HMAC-SHA256 signature for verification:
X-Sendivent-Signature: t=1711800000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-Sendivent-Webhook-Id: unique-attempt-id
Content-Type: application/json
User-Agent: Sendivent-Webhook/1.0Verifying signatures
- Extract
t(timestamp) andv1(signature) from theX-Sendivent-Signatureheader - Compute the expected signature:
HMAC-SHA256(signing_secret, "${t}.${raw_body}") - Compare your computed signature with
v1using constant-time comparison - Reject if the timestamp is older than 5 minutes (replay protection)
javascript
import crypto from 'crypto';
function verifyWebhook(rawBody, signatureHeader, signingSecret) {
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t=')).substring(2);
const signature = parts.find(p => p.startsWith('v1=')).substring(3);
// Check timestamp tolerance (5 minutes)
const age = Math.abs(Date.now() / 1000 - parseInt(timestamp));
if (age > 300) return false;
// Compute expected signature
const expected = crypto
.createHmac('sha256', signingSecret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}POST /v1/webhooks
Create a webhook endpoint. Returns the signing secret — store it securely, it is only shown on creation.
Request body
json
{
"url": "https://your-app.com/webhooks/sendivent",
"enabled_events": ["delivery.sent", "delivery.failed"],
"description": "Production webhook"
}| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS endpoint URL |
enabled_events | string[] | No | Event types to receive (defaults to all) |
description | string | No | Human-readable label |
Response (201)
json
{
"success": true,
"webhook": {
"uuid": "wh-uuid-here",
"url": "https://your-app.com/webhooks/sendivent",
"enabled_events": ["delivery.sent", "delivery.failed"],
"is_active": true,
"description": "Production webhook",
"signing_secret": "whsec_a1b2c3d4e5f6...",
"created_at": "2026-03-30T12:00:00.000Z"
}
}Returns 409 if the application already has a webhook endpoint.
GET /v1/webhooks
Retrieve the webhook endpoint for your application.
Response (200)
json
{
"success": true,
"webhook": {
"uuid": "wh-uuid-here",
"url": "https://your-app.com/webhooks/sendivent",
"enabled_events": ["delivery.sent", "delivery.failed"],
"is_active": true,
"description": "Production webhook",
"created_at": "2026-03-30T12:00:00.000Z",
"updated_at": "2026-03-30T14:00:00.000Z"
}
}Returns 404 if no webhook endpoint is configured.
PATCH /v1/webhooks/
Update a webhook endpoint. Only include fields you want to change.
Request body
json
{
"url": "https://new-endpoint.com/webhooks",
"enabled_events": ["delivery.sent", "delivery.delivered", "delivery.failed"],
"is_active": false,
"description": "Updated webhook"
}All fields are optional.
DELETE /v1/webhooks/
Delete a webhook endpoint. Sendivent will stop sending events immediately.
Response (200)
json
{
"success": true
}Retry Behavior
If your endpoint returns a 5xx status code or times out (10 seconds), Sendivent retries up to 5 times with exponential backoff.
4xx responses are treated as permanent failures and are not retried.
See Also
- Concepts: Deliveries — Understand delivery lifecycle
- API: Notifications — Send notifications
- Sandbox vs Production — Test webhooks safely