Webhooks
Receive real-time notifications for email, contact, and domain events via HTTP webhooks. PostStack sends POST requests to your endpoint when events occur. Includes delivery tracking and replay for failed deliveries.
/webhooksCreate a new webhook endpoint. Subscribe to specific events.
{
"url": "https://yourdomain.com/webhooks/poststack",
"events": [
"email.delivered",
"email.bounced",
"email.complained",
"email.opened",
"email.clicked"
]
}/webhooksList all webhook endpoints for your account.
{
"webhooks": [
{
"id": "wh_abc123def456ghi789",
"url": "https://yourdomain.com/webhooks/poststack",
"events": ["email.delivered", "email.bounced"],
"active": true,
"created_at": "2026-03-23T10:00:00.000Z"
}
]
}/webhooks/:idRetrieve a single webhook endpoint with its configuration.
{
"id": "wh_abc123def456ghi789",
"url": "https://yourdomain.com/webhooks/poststack",
"events": ["email.delivered", "email.bounced", "email.opened"],
"active": true,
"created_at": "2026-03-23T10:00:00.000Z"
}/webhooks/:idUpdate a webhook's URL, events, or enabled status.
{
"url": "https://yourdomain.com/webhooks/v2",
"events": ["email.delivered", "email.bounced", "email.opened"],
"enabled": true
}/webhooks/:idDelete a webhook endpoint. No further events will be delivered.
{
"deleted": true
}/webhooks/:id/testSend a test event to your webhook endpoint to verify it is working correctly.
{
"success": true,
"status_code": 200,
"response_time_ms": 142
}/webhooks/:id/deliveriesList recent webhook delivery attempts with status, response codes, and timestamps.
{
"deliveries": [
{
"id": 1,
"webhook_id": 1,
"event": "email.delivered",
"status_code": 200,
"success": true,
"attempts": 1,
"created_at": "2026-03-23T10:00:02.000Z",
"next_attempt_at": null
},
{
"id": 2,
"webhook_id": 1,
"event": "email.bounced",
"status_code": 500,
"success": false,
"attempts": 3,
"created_at": "2026-03-23T10:05:00.000Z",
"next_attempt_at": "2026-03-23T11:05:00.000Z"
}
],
"pagination": {
"total": 156,
"page": 1,
"per_page": 20,
"total_pages": 8
}
}/webhooks/:id/deliveries/:did/replayReplay a failed webhook delivery. Sends the original event payload to your endpoint again.
{
"success": true
}Webhook Events
Subscribe to any combination of the following events:
Email Events
| Event | Description |
|---|---|
email.sent | Email has been sent to the recipient mail server |
email.delivered | Email was successfully delivered |
email.bounced | Email hard bounced (permanent failure) |
email.soft_bounced | Email soft bounced (temporary failure) |
email.opened | Recipient opened the email (if tracking enabled) |
email.clicked | Recipient clicked a link (if tracking enabled) |
email.complained | Recipient marked the email as spam |
email.unsubscribed | Recipient clicked the unsubscribe link |
email.failed | Email delivery failed |
email.delivery_delayed | Email delivery was delayed |
email.scheduled | Email was scheduled for future delivery |
email.suppressed | Email was suppressed (recipient on suppression list) |
email.inbound | Inbound email received on your domain |
Contact Events
| Event | Description |
|---|---|
contact.created | A new contact was created |
contact.updated | A contact was updated |
contact.deleted | A contact was deleted |
contact.unsubscribed | A contact unsubscribed |
Domain Events
| Event | Description |
|---|---|
domain.created | A new domain was added |
domain.updated | Domain settings were updated |
domain.deleted | A domain was removed |
domain.verified | Domain DNS verification succeeded |
domain.failed | Domain DNS verification failed |
Webhook Payload
Each webhook delivery includes the event type, timestamp, and relevant data:
{
"id": "evt_abc123def456",
"type": "email.delivered",
"timestamp": "2026-03-23T10:00:02.000Z",
"data": {
"email_id": "em_abc123def456ghi789",
"from": "you@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome to PostStack"
}
}Signature Verification
Every webhook request includes an X-PostStack-Signature header. Verify it to ensure the request came from PostStack:
import crypto from 'crypto';
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string,
): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expectedSignature = `sha256=${expected}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature),
);
}
// In your webhook handler:
app.post('/webhooks/poststack', (req, res) => {
const signature = req.headers['x-poststack-signature'];
const body = JSON.stringify(req.body);
if (!verifyWebhookSignature(body, signature, 'whsec_abc123...')) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the event
const event = req.body;
switch (event.type) {
case 'email.delivered':
// Handle delivery
break;
case 'email.bounced':
// Handle bounce
break;
}
res.status(200).json({ received: true });
});