Sending
Webhooks
Receive real-time notifications about delivery events and replies.
Overview
Configure a webhook URL in your Dashboard to receive HTTP POST requests whenever a delivery event occurs. LDM sends a JSON payload to your endpoint with event details.
Events
| Event | Description |
|---|---|
delivery.confirmed | Message confirmed in recipient's inbox. |
delivery.failed | Message bounced or delivery failed. |
reply.received | Recipient replied to your message. |
creative.approved | Creative passed moderation. |
creative.rejected | Creative failed moderation. |
Payload examples
delivery.confirmed
{
"event": "delivery.confirmed",
"message_id": "msg_01HQX7kP...",
"to": "cto@acme.com",
"status": "inbox",
"delivered_at": "2026-04-16T09:00:12Z",
"cost": 0.015
}delivery.failed
{
"event": "delivery.failed",
"message_id": "msg_01HQX7kP...",
"to": "nobody@invalid.test",
"status": "bounce",
"reason": "mailbox_not_found",
"failed_at": "2026-04-16T09:01:00Z"
}reply.received
{
"event": "reply.received",
"message_id": "msg_01HQX7kP...",
"from": "cto@acme.com",
"subject": "Re: Quick question about your infrastructure",
"body": "Thanks for reaching out! Let's schedule a call.",
"received_at": "2026-04-16T10:30:00Z"
}creative.approved
{
"event": "creative.approved",
"creative_id": "cr_9f2a...",
"approved_at": "2026-04-16T08:55:00Z"
}creative.rejected
{
"event": "creative.rejected",
"creative_id": "cr_9f2a...",
"reason": "promotional_content",
"details": "Message contains unsolicited promotional content.",
"rejected_at": "2026-04-16T08:55:00Z"
}Signature verification
Every webhook request includes an X-LDM-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret. Always verify this signature to ensure the request is from LDM.
Node.js verification example
import crypto from 'crypto';
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
const sig = signature.replace('sha256=', '');
return crypto.timingSafeEqual(
Buffer.from(sig, 'hex'),
Buffer.from(expected, 'hex')
);
}
// In your webhook handler:
app.post('/webhooks/ldm', (req, res) => {
const signature = req.headers['x-ldm-signature'] as string;
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
process.env.LDM_WEBHOOK_SECRET!
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the event
const { event, message_id } = req.body;
console.log(`Received ${event} for ${message_id}`);
res.status(200).json({ received: true });
});Retry policy
If your endpoint returns a non-2xx status code or times out, LDM retries the webhook with exponential backoff:
- ●Retry 1 — after 1 minute
- ●Retry 2 — after 5 minutes
- ●Retry 3 — after 30 minutes
After 3 failed attempts, the event is dropped. You can view missed events in the Dashboard webhook logs.