Skip to content
Splashify Pro
Email docsEmail

Webhooks

Webhooks let your app react to email lifecycle events as they happen. Send → Delivery → Open → Click → Bounce → Complaint → Reply — every state transition fires an HTTP POST to your URL with a structured JSON payload.

Two webhook surfaces

Pick whichever fits your integration:

You can use both at once. The account-level URL receives every event regardless of config set; the per-config-set destinations receive their filtered subset additionally. Most customers start with just the account-level URL and add per-config-set destinations later when they need to route per-customer.

Settings with the Webhook URL section open, the endpoint URL https://example.com/webhooks/splashify filled in and the Save webhook URL button
The account-level webhook URL in Settings

Event types fired to your webhook

How it works

Loading diagram

  1. You create a configuration set (a logical grouping for sends).
  2. You add a webhook event destination to that config set, with the URL + secret + the event types you want.
  3. You send emails with configuration_set_name referencing that set.
  4. Every event for those sends gets POSTed to your URL with an HMAC-signed body.
  5. Retries: 5xx + timeout → 1min, 5min, 15min. 4xx → no retry (your URL is broken; we don't waste retries).

Set up a webhook in 3 calls

1. Create a config set

bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets \
  -H "Authorization: Bearer $SPLASHIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production",
    "suppression_options": "BOUNCE_AND_COMPLAINT"
  }'

Save the config_set_id from the response.

2. Add a webhook destination

bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets/$CONFIG_SET_ID/event-destinations \
  -H "Authorization: Bearer $SPLASHIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "production-webhook",
    "destination_type": "WEBHOOK",
    "webhook_url": "https://yourapp.com/webhooks/splashify",
    "webhook_secret": "your-shared-secret-32-chars",
    "matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"]
  }'

Security

Store the webhook_secret somewhere your endpoint can read it (env var). The API will never echo it back — to rotate, PATCH a new value.

3. Reference the config set on send

bash
curl https://api.splashifypro.com/api/v1/partner/email/send \
  -H "Authorization: Bearer $SPLASHIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "[email protected]",
    "to": ["[email protected]"],
    "subject": "Welcome",
    "html_body": "<h1>Hi</h1>",
    "configuration_set_name": "production"
  }'

Within seconds your endpoint receives a Send event, then Delivery, etc.

Payload shape

We follow the AWS SES event-publishing JSON envelope. Code written against AWS SES SNS subscriptions consumes our webhooks by swapping the auth header verification.

json
{
  "eventType": "Delivery",
  "mail": {
    "timestamp": "2026-05-03T12:34:56Z",
    "messageId": "550e8400-e29b-41d4-a716-446655440000",
    "source": "[email protected]",
    "destination": ["[email protected]"]
  },
  "delivery": {
    "timestamp": "2026-05-03T12:34:57Z",
    "recipients": ["[email protected]"],
    "smtpResponse": "250 OK"
  }
}

Every event has the top-level eventType and mail fields. The event-specific payload is nested under a key matching the lowercase event type — delivery, bounce, complaint, etc.

Headers

Every POST carries:

Quick verify in Node

javascript
import crypto from "crypto";

app.post("/webhooks/splashify", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("x-splashify-signature") || "";
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.SPLASHIFY_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).end();
  }

  const event = JSON.parse(req.body.toString());
  // process event...
  res.status(200).end();
});

Next