Webhooks
Webhooks deliver real-time delivery events to your application. When a message is delivered, bounced, opened, or fails, SendPipe sends an HTTP POST to your configured endpoint with the event payload.
How It Works
Register a webhook endpoint URL and select which event types you want to receive. SendPipe signs every webhook payload with HMAC-SHA256 so you can verify authenticity. Each endpoint gets a unique signing secret on creation.
Endpoints
/v1/webhooksCreate a new webhook endpoint with a URL and subscribed event types.
/v1/webhooksList all webhook endpoints for the current project.
/v1/webhooks/:idRemove a webhook endpoint. Pending deliveries will be discarded.
/v1/webhooks/:id/testSend a test event to the endpoint to verify connectivity.
Event Types
Subscribe to one or more of the following event types. Events are scoped by channel -- email events, WhatsApp events, and SMS events use separate prefixes.
| Event | Description |
|---|---|
delivered | Message accepted by the recipient's mail server. |
bounced | Hard bounce -- recipient address does not exist or permanently rejected. |
soft_bounce | Temporary delivery failure (e.g. mailbox full). May be retried. |
complained | Recipient marked the message as spam. |
opened | Recipient opened the email (tracking pixel). |
clicked | Recipient clicked a tracked link in the email. |
failed | All providers exhausted. Message moved to dead-letter queue. |
| Event | Description |
|---|---|
wa_sent | Message sent to WhatsApp servers. |
wa_delivered | Message delivered to the recipient's device. |
wa_read | Recipient read the message (blue ticks). |
wa_failed | WhatsApp delivery failed after all provider retries. |
wa_replied | Recipient replied to the WhatsApp message. |
Signature Verification
Every webhook request includes an X-SendPipe-Signature header containing an HMAC-SHA256 signature of the raw request body, computed using your endpoint's signing secret. Always verify this signature before processing events.
HMAC-SHA256(secret, rawBody) and hex-encode the result.X-SendPipe-Signature header using a timing-safe comparison.Example: Verify Webhook Signature
import crypto from "node:crypto";
const WEBHOOK_SECRET = process.env.SENDPIPE_WEBHOOK_SECRET;
function verifySignature(rawBody, signatureHeader) {
const expected = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(rawBody, "utf-8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(signatureHeader, "hex")
);
}
// Express example
app.post("/webhooks/sendpipe", express.raw({ type: "application/json" }), (req, res) => {
const signature = req.headers["x-sendpipe-signature"];
const rawBody = req.body.toString("utf-8");
if (!verifySignature(rawBody, signature)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(rawBody);
console.log(event.type); // "delivered"
console.log(event.messageId); // "msg_clx4..."
res.status(200).json({ received: true });
});Retry Behavior
If your endpoint returns a non-2xx status code or times out, SendPipe retries the delivery with exponential backoff. After all attempts are exhausted, the event is marked as failed and no further retries occur.
| Attempt | Delay | Cumulative |
|---|---|---|
| 1st retry | 30 seconds | 30s |
| 2nd retry | 2 minutes | ~2.5 min |
| 3rd retry | 10 minutes | ~12.5 min |
| 4th retry | 30 minutes | ~42.5 min |
| 5th retry | 2 hours | ~2.7 hours |
Total: 5 retry attempts over approximately 2.7 hours. After the 5th failure, the event delivery is abandoned.
Testing Your Endpoint
Use the test endpoint to send a synthetic event to your webhook URL. This is useful for verifying your signature validation logic and endpoint connectivity.
curl -X POST https://api.sendpipe.io/v1/webhooks/wh_clx4.../test \
-H "X-SendPipe-Key: sp_live_your_key_here"
# Sends a test "delivered" event to your endpoint URL.
# Response: { "success": true, "statusCode": 200 }