Webhooks

Real-time event notifications for agent mailboxes with HMAC verification.

Overview

Webhooks deliver real-time HTTP POST notifications to your endpoint when events occur in an agent mailbox — new messages, bounces, delivery confirmations, and more.

Create a Webhook

const webhook = await tx.agentEmail.webhooks.create({
  mailbox: mailbox.id,
  url: 'https://yourapp.com/webhooks/agent-email',
  events: ['message.received', 'message.bounced', 'message.delivered'],
  secret: 'whsec_your_signing_secret', // Optional — auto-generated if omitted
});
 
console.log(webhook.id);     // whk_abc123
console.log(webhook.secret); // whsec_...

Event Types

EventDescription
message.receivedNew inbound email received in the mailbox
message.deliveredOutbound email successfully delivered
message.bouncedOutbound email bounced (hard or soft)
message.deferredOutbound email delivery deferred (will retry)
message.complainedRecipient marked email as spam
message.openedRecipient opened the email (if tracking enabled)
message.clickedRecipient clicked a link (if tracking enabled)

Webhook Payload

All events follow a consistent structure:

{
  "id": "evt_abc123",
  "type": "message.received",
  "createdAt": "2025-01-10T14:30:00Z",
  "data": {
    "messageId": "msg_xyz789",
    "mailboxId": "mbx_abc123",
    "from": "user@example.com",
    "to": ["agent@agentmail.usetransactional.com"],
    "subject": "Re: Research Request",
    "textPreview": "Thanks for the update...",
    "threadId": "thd_def456",
    "receivedAt": "2025-01-10T14:30:00Z"
  }
}

For message.bounced events, additional fields are included:

{
  "type": "message.bounced",
  "data": {
    "messageId": "msg_xyz789",
    "bounceType": "hard",
    "bounceCode": "550",
    "bounceMessage": "Mailbox not found",
    "recipient": "invalid@example.com"
  }
}

HMAC Verification

Every webhook request includes an X-Transactional-Signature header containing an HMAC-SHA256 signature. Always verify this signature to ensure the request is authentic.

import crypto from 'crypto';
 
function verifyWebhookSignature(
  payload: string,
  signature: string,
  secret: string,
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
 
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}
 
// In your webhook handler
app.post('/webhooks/agent-email', (req, res) => {
  const signature = req.headers['x-transactional-signature'] as string;
  const isValid = verifyWebhookSignature(
    JSON.stringify(req.body),
    signature,
    process.env.WEBHOOK_SECRET,
  );
 
  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }
 
  // Process the event
  const event = req.body;
  // ...
 
  res.status(200).send('OK');
});

Retry Policy

Failed webhook deliveries (non-2xx response or timeout) are retried with exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failed attempts, the event is dropped and the webhook is marked as failing. You can view failed deliveries in the dashboard.

Manage Webhooks

// List webhooks for a mailbox
const webhooks = await tx.agentEmail.webhooks.list({
  mailbox: mailbox.id,
});
 
// Update a webhook
await tx.agentEmail.webhooks.update(webhookId, {
  events: ['message.received'],
  url: 'https://yourapp.com/webhooks/v2/agent-email',
});
 
// Delete a webhook
await tx.agentEmail.webhooks.delete(webhookId);
 
// Test a webhook (sends a test event)
await tx.agentEmail.webhooks.test(webhookId);

Next Steps