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

POST
/v1/webhooks

Create a new webhook endpoint with a URL and subscribed event types.

GET
/v1/webhooks

List all webhook endpoints for the current project.

DELETE
/v1/webhooks/:id

Remove a webhook endpoint. Pending deliveries will be discarded.

POST
/v1/webhooks/:id/test

Send 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.

Email Events
EventDescription
deliveredMessage accepted by the recipient's mail server.
bouncedHard bounce -- recipient address does not exist or permanently rejected.
soft_bounceTemporary delivery failure (e.g. mailbox full). May be retried.
complainedRecipient marked the message as spam.
openedRecipient opened the email (tracking pixel).
clickedRecipient clicked a tracked link in the email.
failedAll providers exhausted. Message moved to dead-letter queue.
WhatsApp Events
EventDescription
wa_sentMessage sent to WhatsApp servers.
wa_deliveredMessage delivered to the recipient's device.
wa_readRecipient read the message (blue ticks).
wa_failedWhatsApp delivery failed after all provider retries.
wa_repliedRecipient 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.

1Read the raw request body as a string (do not parse JSON first).
2Compute HMAC-SHA256(secret, rawBody) and hex-encode the result.
3Compare the computed hash with the 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.

AttemptDelayCumulative
1st retry30 seconds30s
2nd retry2 minutes~2.5 min
3rd retry10 minutes~12.5 min
4th retry30 minutes~42.5 min
5th retry2 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.

bash
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 }