Skip to content

Documentation

Webhooks.

Get told when a message is delivered, bounces or is marked as spam, instead of asking.

Receiving events

Add an endpoint under Webhooks in the dashboard and choose the events you want. Mailtruss sends each one to your URL as a signed POST with a JSON body. Any 2xx response within 10 seconds counts as delivered.

EventSent when
email.sentHanded to the mail provider.
email.deliveredAccepted by the recipient's mail server.
email.bouncedPermanently rejected by the recipient's server.
email.complainedThe recipient marked it as spam.
email.delivery_delayedTemporarily deferred; delivery is still being retried.
email.openedThe message was opened (needs open tracking; approximate, as some mail apps preload images).
email.clickedA tracked link was clicked (needs click tracking on the domain).
email.failedWe could not send it (for example, an unverified domain).
email.receivedAn email arrived at one of your receiving domains.

Every body has the event name, the time it was created and a data object. data always holds email_id, the ID shown on the email’s page and returned when you sent it. Delivery events also list the recipients (to) and the details recorded for the event, such as a bounce’s type and the receiving server’s own words.

Request body
{
"type": "email.bounced",(highlighted line)
"created_at": "2026-11-14T09:41:09.930Z",
"data": {
"email_id": "ad20b8db-731c-4997-87cd-0a41a5d84f5a",
"to": ["zainab.yusuf56@contoso.org"],
"bounceType": "Permanent",
"diagnostic": "smtp; 550 5.7.1 Message rejected due to local policy"
}
}

Verifying the signature

Anyone can post to a public URL, so check that a request is ours before you act on it. Each request carries three headers in the Standard Webhooks scheme. The same values are also sent as svix-id, svix-timestamp and svix-signature, so code written for Svix-signed webhooks works unchanged.

Headers
POST /webhooks/mailtruss HTTP/1.1
content-type: application/json
webhook-id: 6f1c0e0a-…(highlighted line)
webhook-timestamp: 1794649269(highlighted line)
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNd9cJ2m…(highlighted line)
  • webhook-signature holds one or more space-separated v1,<signature> values. The signature is an HMAC-SHA256, in base64, of {webhook-id}.{webhook-timestamp}.{raw body}, keyed with your endpoint’s signing secret. Accept the request if any of them matches.
  • Reject a request whose webhook-timestamp is more than five minutes from your clock. This stops an old, captured request from being replayed.
  • When you rotate a secret, requests carry a signature for the old and the new secret for a while, so verification keeps working while you deploy.
app/api/webhooks/mailtruss/route.ts
import { verifyWebhook } from "mailtruss";
​
// `body` must be the raw request body, exactly as received.
export async function POST(req: Request) {
const event = await verifyWebhook(await req.text(), req.headers, process.env.MAILTRUSS_WEBHOOK_SECRET!);(highlighted line)
if (event.type === "email.bounced") await suppress(event.data.email_id);
return new Response(null, { status: 200 });
}

verifyWebhook in the Node.js client checks the signature and the timestamp and returns the parsed event. Endpoints created before 14 November 2026 have a secret in an older format; verifyWebhook accepts both, so prefer it if you are not sure which you have.

When your endpoint is down

A response that is not 2xx, a timeout after 10 seconds, or a connection error counts as a failure. Redirects are not followed, so give the final URL. A failed delivery is tried again after 30 seconds, 2 minutes, 10 minutes, 1 hour, 6 hours, 24 hours, each wait counted from the previous attempt. If the last retry also fails, the delivery is marked Gave up and is not tried again by itself.

Each endpoint’s page in the dashboard lists its deliveries with the response your server gave, and Replay sends one again once you have fixed the problem. An endpoint whose last three deliveries failed is marked Failing, and you get an alert in the dashboard.

Last updated

Something wrong or unclear on this page?

Report an issue