Skip to content

Webhooks

Configure webhook endpoints to receive real-time email delivery events from Optail.

List all webhook endpoints for the organization.

{
"data": [
{
"id": "wh-uuid",
"organizationId": "org-uuid",
"url": "https://yourapp.com/webhooks/optail",
"events": ["DELIVERED", "BOUNCED", "COMPLAINED", "OPENED", "CLICKED"],
"isActive": true,
"createdAt": "2025-01-10T08:00:00Z",
"updatedAt": "2025-01-10T08:00:00Z"
}
]
}

Create a new webhook endpoint. The URL is validated for reachability before creation. A signing secret is generated and returned once — store it securely.

FieldTypeRequiredDescription
urlstringYesHTTPS URL that will receive webhook POST requests.
eventsstring[]YesEvent types to subscribe to (at least one).
isActivebooleanNoWhether the endpoint is active (default: true).
EventDescription
QUEUEDEmail accepted and queued for delivery.
SENTEmail handed off to the provider.
DELIVEREDProvider confirmed delivery to recipient’s mailbox.
OPENEDRecipient opened the email (tracking pixel).
CLICKEDRecipient clicked a link in the email.
BOUNCEDEmail bounced (hard, soft, or block).
COMPLAINEDRecipient marked the email as spam.
UNSUBSCRIBEDRecipient unsubscribed via the unsubscribe link.
DROPPEDEmail was dropped before delivery (e.g., invalid address).
DEFERREDDelivery was temporarily deferred by the receiving server.

Status: 201 Created

{
"data": {
"id": "wh-uuid",
"organizationId": "org-uuid",
"url": "https://yourapp.com/webhooks/optail",
"events": ["DELIVERED", "BOUNCED", "COMPLAINED"],
"isActive": true,
"createdAt": "2025-01-10T08:00:00Z",
"updatedAt": "2025-01-10T08:00:00Z",
"secret": "whsec_a1b2c3d4e5f6..."
}
}

Update a webhook endpoint’s URL, events, or active status. If the URL changes, it is re-validated for reachability.

FieldTypeRequiredDescription
urlstringNoNew webhook URL.
eventsstring[]NoNew event subscriptions.
isActivebooleanNoEnable or disable the endpoint.

Delete a webhook endpoint.

Send a test webhook event to verify your endpoint is receiving data correctly.

{
"success": true,
"statusCode": 200,
"responseTime": "under 10s"
}

When an event occurs, Optail sends a POST request to your endpoint with the following format:

{
"event": "DELIVERED",
"timestamp": "2025-01-15T10:00:03Z",
"data": {
"messageId": "msg-uuid",
"eventType": "DELIVERED",
"recipient": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Welcome!"
}
}
HeaderDescription
Content-Typeapplication/json
X-Optail-SignatureHMAC-SHA256 signature for payload verification.
X-Optail-TimestampUnix timestamp (seconds) of when the webhook was sent.
X-Optail-EventEvent type (e.g., DELIVERED).

Every webhook request includes a signature in the X-Optail-Signature header. You should verify this signature to confirm the request came from Optail and was not tampered with.

The signature is computed as:

HMAC-SHA256(secret, "${timestamp}.${payload}")

Where timestamp is the X-Optail-Timestamp header value and payload is the raw JSON request body.

import { verifyWebhookSignature } from 'optail';
app.post('/webhooks/optail', (req, res) => {
const signature = req.headers['x-optail-signature'];
const timestamp = req.headers['x-optail-timestamp'];
const rawBody = req.body; // raw string, not parsed JSON
const isValid = verifyWebhookSignature(
rawBody,
signature,
process.env.OPTAIL_WEBHOOK_SECRET,
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(rawBody);
// Process the event...
res.status(200).send('OK');
});
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expectedBuf = Buffer.from(expected, 'utf-8');
const signatureBuf = Buffer.from(signature, 'utf-8');
if (expectedBuf.length !== signatureBuf.length) {
return false;
}
return timingSafeEqual(expectedBuf, signatureBuf);
}
  • Always verify signatures before processing webhook events.
  • Respond with 2xx quickly — process events asynchronously if your logic is slow.
  • Handle duplicates — Optail may retry delivery, so your handler should be idempotent.
  • Check the timestamp — reject events older than 5 minutes to prevent replay attacks.