Webhooks
Receive real-time HTTP notifications about email delivery events. Configure webhook endpoints per message stream to track deliveries, bounces, opens, and more.
Event Types
When creating a webhook, specify which events you want to receive. Use * to subscribe to all events.
| Event | Description |
|---|---|
| sent | The SMTP relay accepted the message for delivery |
| delivered | Recipient mail server accepted the message |
| bounced | Hard or soft bounce from recipient server |
| complained | Recipient marked the email as spam |
| rejected | Rejected by the SMTP relay or suppressed before sending |
| failed | Send attempt failed after all retries exhausted |
| opened | Recipient opened the email |
| clicked | Recipient clicked a link in the email |
| * | Wildcard - receive all event types |
Create a Webhook
Register a webhook URL to receive events for a specific message stream. Requires JWT Bearer authentication.
Request Body
The HTTPS endpoint URL that will receive webhook payloads.
Array of event types to subscribe to. Use ["*"] for all events.
{
"url": "https://yourapp.com/webhooks/postject",
"events": ["delivered", "bounced", "complained"]
}Webhook Payload
When an event occurs, Postject sends a POST request to your webhook URL with the following JSON body:
{
"id": "msg_01hy4z9k2m...",
"type": "delivered",
"to": "user@example.com",
"subject": "Welcome aboard!",
"metadata": {},
"timestamp": "2024-01-15T10:30:00.000Z"
}Signature Verification
All webhook requests include an HMAC-SHA256 signature in the X-Postject-Signature header to verify authenticity and prevent tampering.
Getting Your Webhook Secret
When you create a webhook, the response includes a unique secret. Store this securely . you'll need it to verify webhook signatures.
{
"id": "wh_abc123",
"url": "https://yourapp.com/webhooks/postject",
"events": ["delivered", "bounced"],
"secret": "whsec_a3f7d9e2c1b8a4f6e8d3c9b7a5f1e3d2",
"createdAt": "2025-07-15T10:30:00.000Z"
}Verifying Signatures
Compute an HMAC-SHA256 digest of the raw request body using your webhook secret, then compare it to the signature header:
import crypto from 'crypto';
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
const expectedSignature = `sha256=${hmac.digest('hex')}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// In your webhook handler:
app.post('/webhooks/postject', (req, res) => {
const signature = req.headers['x-postject-signature'];
const isValid = verifyWebhookSignature(
req.body,
signature,
'whsec_your_secret_here'
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
// Process webhook event
console.log('Event:', req.body.type);
res.status(200).send('OK');
});Security Note
Always verify webhook signatures before processing events. This prevents attackers from sending forged webhook requests to your endpoint.
Retry Policy
If your webhook endpoint returns a non-2xx HTTP status or times out, Postject automatically retries delivery with exponential backoff up to 8 times over 30 days.
Retry Schedule
| Attempt | Delay | Total Time Since First Attempt |
|---|---|---|
| 1 | Immediate | . |
| 2 | 1 minute | 1 min |
| 3 | 5 minutes | 6 min |
| 4 | 30 minutes | 36 min |
| 5 | 2 hours | ~3 hours |
| 6 | 12 hours | ~15 hours |
| 7 | 1 day | ~2 days |
| 8 | 3 days | ~5 days |
After 8 failed attempts, the webhook delivery is marked as permanently failed and logged. You can view failed deliveries in the webhook logs.
Request Timeout
Each webhook request has a 10 second timeout. If your endpoint doesn't respond within this window, the delivery is considered failed and will be retried according to the schedule above.
Delivery Logs
View detailed logs of all webhook delivery attempts, including successful deliveries, failures, and retry history.
Query Parameters
Number of logs to return (max 1000, default 100)
Pagination offset
{
"logs": [
{
"id": "log_xyz789",
"webhookId": "wh_abc123",
"eventType": "delivered",
"status": "success",
"statusCode": 200,
"attempt": 1,
"response": "OK",
"duration": 145,
"createdAt": "2025-07-15T10:30:00.000Z",
"nextRetry": null
},
{
"id": "log_def456",
"webhookId": "wh_abc123",
"eventType": "bounced",
"status": "failed",
"statusCode": 500,
"attempt": 3,
"response": "Internal Server Error",
"duration": 10000,
"createdAt": "2025-07-15T09:15:00.000Z",
"nextRetry": "2025-07-15T09:45:00.000Z"
}
],
"total": 1234
}Test a Webhook
Send a test event to verify your webhook endpoint is working correctly.
This sends a test delivered event to your webhook URL with sample data. The response includes the HTTP status code and response body from your endpoint.
{
"success": true,
"statusCode": 200,
"response": "OK",
"duration": 123
}List Webhooks
Returns all registered webhooks for a given stream. Requires JWT Bearer authentication.
Delete a Webhook
Permanently removes a webhook. Events will no longer be sent to the registered URL. Requires JWT Bearer authentication.