Webhooks
API base URL: https://api.relayloft.com
Add endpoints under Webhooks in the portal. Each event is a POST of JSON to your https URL, signed the same way as Resend's (Svix).
Events
email.scheduled, email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained, email.failed, email.suppressed, and with tracking on, email.opened and email.clicked.
{
"type": "email.bounced",
"created_at": "2026-09-25T10:00:00.000Z",
"data": {
"email_id": "…",
"created_at": "2026-09-25T09:59:58.000Z",
"from": "hello@mail.yourcompany.com",
"to": ["user@example.com"],
"subject": "Welcome",
"tags": [{ "name": "flow", "value": "signup" }],
"bounce": { "type": "Permanent", "subType": "General", "message": "…" }
}
}Extra fields by type: bounce (type, subType, message), complaint (type), delay (type, message), failed (reason), suppressed (message), click (ipAddress, link, timestamp, userAgent: camelCase, as Resend sends it). email.sent lists any suppressed_recipients it skipped.
Check the signature
Every request has svix-id, svix-timestamp and svix-signature headers. Verify them against the raw body with your endpoint's signing secret (whsec_..., shown once when you create or rotate it):
import { Webhook } from "svix"; // or resend.webhooks.verify(...)
const wh = new Webhook(process.env.RELAYLOFT_WEBHOOK_SECRET);
const event = wh.verify(rawBody, {
"svix-id": req.headers["svix-id"],
"svix-timestamp": req.headers["svix-timestamp"],
"svix-signature": req.headers["svix-signature"],
});After you rotate the secret, the old one keeps working for 24 hours: during that time each request carries both signatures. Use svix-id to drop duplicates.
Retries
Answer with any 2xx within 10 seconds. Anything else (including a redirect) is retried with growing gaps, for about 20 hours. An endpoint that fails for about 20 hours straight is turned off and your Owner is emailed; turn it back on in the portal. You can replay any delivery or send a test event from the portal.
Endpoint rules
https on the standard port, a public host name (not an IP address or an internal name), no user name or password in the URL. Checked when saved and before every delivery.