Skip to content
Splashify Pro
Email docsEmail

Common Recipes

Production-grade patterns that save you reinventing the wheel.

Idempotent transactional sends

Replays of the same logical send (e.g. payment-receipt for the same charge ID) shouldn't trigger duplicate emails. Stamp a stable X-Idempotency-Key header derived from your business key, and de-dup on your side before calling /send:

javascript
const sentKey = await redis.get(`email:sent:${chargeId}`);
if (sentKey) return; // already emailed

const res = await client.emails.send({...});
await redis.set(`email:sent:${chargeId}`, res.results[0].messageId, { EX: 86400 });

We don't currently honor an Idempotency-Key header server-side (roadmap), so the dedup happens at your layer.

Per-recipient personalization at scale

For bulk sends with per-recipient variables, use /send-bulk:

javascript
await client.emails.sendBulk({
  from: "[email protected]",
  templateName: "newsletter-june",
  defaultTemplateData: { campaign: "june-2026" },
  destinations: users.map(u => ({
    to: [u.email],
    replacementData: {
      first_name: u.firstName,
      unsubscribe_url: `https://yoursite.com/u/${u.token}`,
    },
  })),
});

50 destinations × 50 recipients × 500 total per request. For larger volumes, chunk into multiple calls — there's no per-account rate limit on bulk-send concurrency, only the per-account per-second peak rate.

Dynamic from-name

Set the from field as "Display Name <[email protected]>" and recipients see "Display Name" in their inbox:

json
{
  "from": "Sarah from Acme <[email protected]>",
  ...
}

Domain still has to be verified. Display name is unverified — recipients see whatever you put there.

Custom MAIL FROM domain (return-path)

Sets the bounce return-path to a custom subdomain so DMARC alignment includes both DKIM and SPF. Lands on roadmap. Until then, the return-path is [email protected] and DMARC alignment relies on DKIM only (p=quarantine is fine; p=reject may need DMARC relaxed alignment).

Per-customer attribution

If you're sending on behalf of multiple downstream customers, create a configuration set per customer and reference it on every send:

javascript
await client.emails.send({
  from: "[email protected]",
  to: ["[email protected]"],
  configurationSetName: "customer_acme_corp",
  ...
});

Stats roll up per config set — GET /stats?config_set_id=.... Each config set can also have its own webhook destination so events for Acme go to one URL and events for Globex go to another.

Hard-bounce auto-list-cleaning

Hard bounces are added to your suppression list automatically. To keep your application's email list in sync, listen for the Bounce webhook event:

javascript
app.post("/webhooks/splashify", async (req, res) => {
  const sig = req.header("x-splashify-signature");
  if (!verifyHMAC(req.rawBody, sig, process.env.SPLASHIFY_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }

  if (req.body.eventType === "Bounce" && req.body.bounce.bounceType === "Permanent") {
    const email = req.body.bounce.bouncedRecipients[0].emailAddress;
    await db.users.updateOne({ email }, { $set: { emailBouncedHard: true } });
  }
  res.status(200).end();
});

Now your signup form / re-engagement campaigns can skip these addresses up-front instead of burning send quota.

Retry on 5xx

Network blips and brief upstream issues should retry; auth/quota errors shouldn't. Pseudocode:

javascript
async function sendWithRetry(payload, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    const res = await client.emails.send(payload);
    if (res.success) return res;
    const code = res.error;

    // Don't retry these — won't get better with time.
    if (["INVALID_REQUEST", "FROM_NOT_VERIFIED", "INSUFFICIENT_BALANCE", "MISSING_AUTH"].includes(code)) {
      throw new Error(`${code}: ${res.message}`);
    }
    // Backoff on the rest.
    await sleep(1000 * Math.pow(2, i));
  }
  throw new Error("send_failed_after_retries");
}

Webhooks that survive your deploys

Stamp every event in your local DB before processing — duplicate webhook deliveries (which happen on retries) are idempotent:

javascript
const eventID = req.header("x-splashify-delivery-id");
const inserted = await db.events.insertOne({
  _id: eventID,
  ...req.body,
}, { ignoreDuplicates: true });
if (!inserted.insertedCount) return res.status(200).end(); // dedup
// ... process

X-Splashify-Delivery-ID is a fresh UUID per delivery attempt — even a retried event keeps the same ID.

Cold-start IP warm-up

New production accounts inherit warm sending infrastructure with established reputation across major mailbox providers. You don't need to manually warm up.

Dedicated IPs (roadmap) require warm-up — typically 2 weeks of gradually increasing volume. We'll publish a warm-up calculator when dedicated IPs ship.