Platform
Webhooks
Receive real-time HTTP notifications when email events occur — delivered, bounced, opened, clicked, or marked as spam.
Create a Webhook
Navigate to Settings → Webhooks and click Add Endpoint. Enter a publicly accessible HTTPS URL that Render25 will POST events to.
Warning
The endpoint URL must use HTTPS. HTTP endpoints will be rejected.
Event Types
| Event | Description |
|---|---|
email.delivered | Message accepted by the recipient mail server |
email.bounced | Delivery permanently failed (hard bounce) |
email.soft_bounced | Delivery temporarily failed (soft bounce) |
email.opened | Recipient opened the email (pixel tracked) |
email.clicked | Recipient clicked a tracked link |
email.complained | Recipient marked the email as spam |
email.unsubscribed | Recipient clicked an unsubscribe link |
Payload Format
Each webhook event is delivered as a JSON POST request. All events share a common envelope:
webhook payload
{
"id": "evt_01J3K9X7YDBTKMR5NXQZG4HW",
"type": "email.delivered",
"created_at": "2024-09-12T10:42:05Z",
"data": {
"message_id": "msg_01J3K9X7YDBTKMR5NXQZG4HWFP",
"to": "[email protected]",
"subject": "Welcome!",
"domain": "yourdomain.com",
"tags": ["welcome"]
}
}Signature Verification
Every webhook request includes a Render25-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing the payload.
verify-webhook.ts
import crypto from "crypto";
function verifyWebhook(
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 route handler:
const sig = request.headers.get("Render25-Signature") ?? "";
const body = await request.text();
if (!verifyWebhook(body, sig, process.env.WEBHOOK_SECRET!)) {
return new Response("Unauthorized", { status: 401 });
}Always verify signatures
Processing webhook events without signature verification exposes your application to spoofed requests. Never skip this step in production.
Retry Policy
If your endpoint returns a non-2xx status code or times out (timeout is 10s), Render25 retries the delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 5 hours — final attempt |
