# Splashify Pro Email docs: complete documentation > Official documentation for Splashify Pro Email. Build on the Email API: verify sending identities, send transactional and bulk email, manage templates and suppression, and configure webhooks. Generated from https://email-docs.splashifypro.com. Sections follow the site's own reading order: the guides first, then the API reference and webhooks, then the legal pages. ================================================================================ # Splashify Pro Email API Section: Guides › Documentation URL: https://email-docs.splashifypro.com/ ================================================================================ > Build email-sending applications on Splashify Pro. AWS-SES-shaped API, webhooks, and SMTP relay. # Splashify Pro Email API _Guides and the full API reference for Splashify Pro Email. Verify a domain, send your first email, then add templates, webhooks and the SMTP relay as you need them._ Splashify Pro Email is an email service for developers. The Email API gives you the building blocks to ship transactional and marketing email at scale: verified sending identities, configuration sets, templates, suppression lists, real-time webhooks, and deliverability-grade reputation tracking. The API is shaped like AWS SES. If you've integrated against AWS SES before, you'll feel at home: sending an email, configuring an event destination, or polling send statistics each map onto a familiar endpoint with the same field names. ## Your first email, step by step 1. **Create your account and an API key**: Sign up at [email.splashifypro.com](https://email.splashifypro.com/signup), then generate a key under **Settings → API Keys**. See [Authentication](/getting-started/authentication). 2. **Verify a sending identity**: Prove you own the domain, or the single address, you send from. See [Sending Identities](/knowledge-base/concepts/sending-identities) and [Create identity](/api-reference/identities/create). 3. **Send an email**: One request is enough. Follow the [Quickstart](/getting-started/quick-start), or pick [Node.js](/getting-started/node-quickstart), [Python](/getting-started/python-quickstart), [PHP](/getting-started/php-quickstart), [Go](/getting-started/go-quickstart), [cURL](/getting-started/curl-quickstart) or [SMTP Relay](/getting-started/smtp-quickstart). 4. **See what happened to it**: Get delivery, bounce, open and click events on your own URL. See [Webhooks](/webhooks). 5. **Move out of the sandbox**: Every new account starts in sandbox mode, with a daily sending limit. See [Sandbox vs Production](/knowledge-base/concepts/sandbox). ## Base URL All API requests are made to: ``` https://api.splashifypro.com/api/v1/partner/email ``` ## Authentication Every API request must carry your secret API key in the `Authorization` header: ```bash Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` Generate a key in Splashify Pro Email at [email.splashifypro.com](https://email.splashifypro.com) under **Settings → API Keys**. The key is shown once, so store it in a secure secret manager. > **Security:** Treat your API key like a password. Never embed it in > client-side code, mobile apps, or public repositories. ## Quick example Send a transactional email with two lines of curl: ```bash curl https://api.splashifypro.com/api/v1/partner/email/send \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "alerts@yourcompany.com", "to": ["customer@example.com"], "subject": "Your order has shipped", "html_body": "
Tracking: ABC123
", "text_body": "Tracking: ABC123" }' ``` Response: ```json { "success": true, "results": [ { "recipient": "customer@example.com", "message_id": "f9c3a2b1-...", "status": "queued" } ] } ``` ## API surface at a glance | Action | Endpoint | AWS SES equivalent | |---|---|---| | Send transactional | `POST /send` | `SendEmail` | | Send raw MIME | `POST /send-raw` | `SendRawEmail` | | Send templated | `POST /send-template` | `SendTemplatedEmail` | | Bulk templated | `POST /send-bulk` | `SendBulkTemplatedEmail` | | Verify domain / address | `POST /identities` | `CreateEmailIdentity` | | Configuration sets | `/configuration-sets` | `CreateConfigurationSet`... | | Event destinations | `/configuration-sets/:id/event-destinations` | `CreateConfigurationSetEventDestination` | | Templates | `/templates` | `CreateEmailTemplate`... | | Suppression list | `/suppression` | `PutSuppressedDestination`... | | Send quota | `GET /quotas` | `GetSendQuota` | | Send statistics | `GET /stats` | `GetSendStatistics` | | Reputation | `GET /reputation` | `GetAccountReputation` | | Production access | `POST /production-access` | Submit support case | ## Response format Success responses always include `success: true`: ```json { "success": true, "data": { ... } } ``` Error responses carry a stable `error` code + a human-readable `message`: ```json { "success": false, "error": "INVALID_REQUEST", "message": "from address is not on a verified identity" } ``` ## HTTP status codes | Code | Meaning | |---|---| | `200` | Success | | `201` | Resource created | | `400` | Bad request: fix your inputs | | `401` | Missing / invalid API key | | `402` | Insufficient wallet balance: top up | | `403` | Sending paused, sandbox cap reached, or feature locked | | `404` | Resource not found | | `409` | Conflict (duplicate name, etc.) | | `429` | Rate limit hit: back off | | `500` | Server error: retry with backoff | | `503` | Database / dependency unavailable | ## Get started - [Getting Started](/getting-started): First-send walkthrough. - [Concepts](/knowledge-base/concepts): Sending identities, config sets, sandbox. - [API Reference](/api-reference): Every endpoint. - [Webhooks](/webhooks): Receive delivery events. - [Deliverability](/knowledge-base/deliverability): SPF/DKIM/DMARC and reputation. - [Pricing](/knowledge-base/pricing): Flat ₹0.03 per email. - [SMTP Relay](/getting-started/smtp-quickstart): Send from any framework that speaks SMTP. - [Build with AI](/build-with-ai): The MCP server, the SDKs and common recipes. - [Legal](/legal): Terms, privacy and the sending policies. ## Need help? - Read the [FAQ](/knowledge-base/faq) and the [error reference](/knowledge-base/errors). - Email [support@splashifypro.in](mailto:support@splashifypro.in). ================================================================================ # Getting Started Section: Guides › Documentation URL: https://email-docs.splashifypro.com/getting-started ================================================================================ > Send your first email through the Splashify Pro Email API in 5 minutes. # Getting Started This guide walks you through the steps to send your first email through the Splashify Pro Email API. ## Prerequisites 1. A Splashify Pro Email account ([sign up free](https://email.splashifypro.com/signup)) 2. An API key (generated in Splashify Pro Email) 3. A domain you control (for production sends — sandbox sends work without a verified domain but only to addresses you've verified) ## Step 1 — Sign up and get an API key If you don't have an account: 1. Visit [email.splashifypro.com/signup](https://email.splashifypro.com/signup) 2. Enter your email, mobile number (with country code), and a password 3. Verify the OTP sent to **both** your email and WhatsApp 4. Log in To create an API key: 1. Go to **Settings → API Keys** 2. Click **Generate API Key** 3. Copy and securely store the key — it is only shown once 4. Use it as the Bearer token in the `Authorization` header on every API request ## Step 2 — Verify a sending identity Before you can send from an address, you must verify ownership of the domain or the email address itself. Domain verification is preferred because it covers any address at that domain. ```bash curl https://api.splashifypro.com/api/v1/partner/email/identities \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "identity_type": "DOMAIN", "identity_value": "yourcompany.com" }' ``` The response includes the three DNS records you need to publish: ```json { "success": true, "identity_type": "DOMAIN", "identity_value": "yourcompany.com", "status": "PENDING", "dns_records": { "spf": { "type": "TXT", "hostname": "yourcompany.com", "value": "v=spf1 include:_spf.mail.splashifypro.com ~all" }, "dkim": { "type": "CNAME", "hostname": "splashify._domainkey.yourcompany.com", "value": "splashify._domainkey.mail.splashifypro.com" }, "dmarc": { "type": "TXT", "hostname": "_dmarc.yourcompany.com", "value": "v=DMARC1; p=quarantine; rua=mailto:dmarc@splashifypro.com" } } } ``` Publish all three records on your DNS provider and trigger a re-check: ```bash curl -X POST https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com/verify \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" ``` When `"status": "VERIFIED"` comes back, you can send from any address ending in `@yourcompany.com`. ## Step 3 — Send a transactional email ```bash curl https://api.splashifypro.com/api/v1/partner/email/send \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "from": "hello@yourcompany.com", "to": ["customer@example.com"], "subject": "Welcome", "html_body": "Thanks for signing up.
", "text_body": "Welcome aboard. Thanks for signing up." }' ``` The response carries the `message_id` you can use to poll delivery status: ```json { "success": true, "results": [ { "recipient": "customer@example.com", "message_id": "550e8400-e29b-41d4-a716-446655440000", "status": "queued" } ] } ``` ## Step 4 — Watch delivery status Poll the message status: ```bash curl https://api.splashifypro.com/api/v1/partner/email/emails/550e8400-e29b-41d4-a716-446655440000 \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" ``` Or — better — set up a [webhook](/webhooks) that we POST to whenever the message transitions through `Send → Delivery → Open → Click / Bounce / Complaint`. ## Step 5 — Move out of sandbox New accounts start in **sandbox mode**: - 200 emails/day cap - 1 email/sec peak send rate - Can only send to verified-recipient addresses To go live, request **production access**: ```bash curl -X POST https://api.splashifypro.com/api/v1/partner/email/production-access \ -H "Authorization: Bearer $SPLASHIFY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "use_case": "Transactional emails for our SaaS application: signup confirmations, password resets, payment receipts.", "email_volume_estimate": "5000-15000 per day", "has_unsubscribe_method": true, "has_consent_proof": true }' ``` Approved requests lift sandbox + bump your daily quota to 50,000 + peak rate to 14/sec. Most requests are reviewed within 24 business hours. ## Next steps - [**Authentication →**](/getting-started/authentication) — API key best practices - [**Concepts →**](/concepts) — configuration sets, event destinations, suppression - [**Webhooks →**](/webhooks) — real-time delivery events - [**API Reference →**](/api-reference) — full endpoint reference ================================================================================ # Quick Start Section: Guides › Documentation URL: https://email-docs.splashifypro.com/getting-started/quick-start ================================================================================ > Send your first email in under 60 seconds. # Quick Start Send your first email in under 60 seconds. Try the form below — the request fires against your real API key on save. > **Try it on the web page:** `POST /api/v1/partner/email/send` Send a transactional email to one recipient. ## What happens on send ```mermaid sequenceDiagram participant You as Your app participant API as Splashify API participant MX as Recipient MX participant Hook as Your webhook You->>API: POST /partner/email/send API-->>You: 200 { message_id, status: queued } API->>MX: STARTTLS + DKIM-signed message MX-->>API: 250 OK API->>Hook: POST event (Send) API->>Hook: POST event (Delivery) Note over MX,Hook: Bounce / Complaint events follow async ``` The API responds immediately with a `message_id`. Behind the scenes we: 1. **Look up your verified identity.** From-address must be on a verified domain (or be a verified email address). 2. **Check the suppression list.** Recipients on your account's suppression list are rejected with `status: rejected` — no SMTP attempt is made and you're not billed. 3. **Deduct ₹0.03 from your wallet.** First 200/day are free in sandbox. 4. **Sign with DKIM** using a key that resolves through your domain's CNAME at `splashify._domainkey.Welcome aboard.
" }' ``` ## What's next - **Verify your domain →** [Identities](/api-reference/identities/create) - **Set up webhooks →** [Webhooks](/webhooks) - **Use templates →** [Templates](/api-reference/templates/create) - **Bulk send →** [SendBulk](/api-reference/emails/send-bulk) - **Move out of sandbox →** [Production access](/api-reference/production-access/submit) ================================================================================ # Authentication Section: Guides › Documentation URL: https://email-docs.splashifypro.com/getting-started/authentication ================================================================================ > Bearer-token authentication, API-key security, and rotation. # Authentication The Splashify Pro Email API uses Bearer-token authentication. Every request must carry a valid API key in the `Authorization` header. ## Generating an API key 1. Log in to [email.splashifypro.com](https://email.splashifypro.com) 2. Navigate to **Settings → API Keys** 3. Click **Generate API Key** 4. Copy the key — it is shown **once**. Lose it and you'll need to regenerate. API keys carry the prefix `pk_live_` and are 64 characters long. ## Using your key Set the `Authorization` header on every request: ```http Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` cURL: ```bash curl https://api.splashifypro.com/api/v1/partner/email/quotas \ -H "Authorization: Bearer pk_live_..." ``` Node: ```js fetch("https://api.splashifypro.com/api/v1/partner/email/quotas", { headers: { Authorization: `Bearer ${process.env.SPLASHIFY_API_KEY}` }, }); ``` Python: ```python import requests, os r = requests.get( "https://api.splashifypro.com/api/v1/partner/email/quotas", headers={"Authorization": f"Bearer {os.environ['SPLASHIFY_API_KEY']}"}, ) ``` ## Rate limits API keys are rate-limited per account, not per key. Defaults: - **Sandbox:** 1 send/sec, 200 sends/day - **Production:** 14 sends/sec (configurable per account), 50,000 sends/day (configurable per account) Rate-limit responses come back as `429 Too Many Requests`. Retry with exponential backoff. ## Key security best practices - **Never embed in client-side code.** API keys go on your server, never in browser JS, mobile apps, or public repos. - **Use environment variables.** Most CI / hosting platforms support secret env vars. `.env` files should be `.gitignore`'d. - **Rotate periodically.** Regenerate keys every 90 days at minimum. - **Use one key per environment.** Separate keys for staging / production make blast-radius cleanup easier. ## Revoking a compromised key 1. Go to **Settings → API Keys** 2. Find the compromised key 3. Click **Revoke** Revocation is immediate. New requests with the revoked key get `401 Unauthorized` within ~5 seconds. ## Authentication errors | Status | Code | Cause | |---|---|---| | 401 | `MISSING_AUTH` | No `Authorization` header | | 401 | `INVALID_KEY_FORMAT` | Key doesn't match `pk_live_...` | | 401 | `KEY_NOT_FOUND` | Key was revoked or never existed | | 401 | `KEY_INACTIVE` | Account suspended | | 403 | `IP_BLOCKED` | Caller's IP is on your account's IP allowlist | ================================================================================ # Node.js Quickstart Section: Guides › Quickstarts URL: https://email-docs.splashifypro.com/getting-started/node-quickstart ================================================================================ > Send your first email from Node.js in 60 seconds. # Node.js Quickstart Send transactional and marketing email from Node.js. Works on every runtime — Node 18+, Bun, Deno, and edge environments (Vercel, Cloudflare Workers, etc.). ## 1. Install We don't ship a Node SDK yet — use any HTTP client. `fetch` is built into Node 18+. ```bash # No install needed if you're on Node 18+ ``` ## 2. Set your API key ```bash export SPLASHIFY_API_KEY="pk_live_..." ``` ## 3. Send your first email ```js const res = await fetch( "https://api.splashifypro.com/api/v1/partner/email/send", { method: "POST", headers: { "Authorization": `Bearer ${process.env.SPLASHIFY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ from: "hello@yourcompany.com", to: ["customer@example.com"], subject: "Welcome to our app", html_body: "Your account is ready.
", text_body: "Welcome. Your account is ready.", }), }, ); const data = await res.json(); console.log(data.results[0].message_id); ``` ## 4. Handle errors ```js if (!res.ok) { const err = await res.json(); switch (err.error) { case "INVALID_REQUEST": // Bad input — read err.message break; case "FROM_NOT_VERIFIED": // Verify your domain at /identities first break; case "SUPPRESSED_RECIPIENT": // Recipient is on your suppression list break; case "INSUFFICIENT_BALANCE": // Recharge your wallet break; default: console.error(err.message); } } ``` ## 5. With React Email [React Email](https://react.email) renders HTML email from React components. You author your template as JSX, render it server-side, and pass the HTML to `/send`. ```bash npm install @react-email/components @react-email/render ``` ```jsx import { Html, Button, Text } from "@react-email/components"; import { render } from "@react-email/render"; function Welcome({ name }) { return (Welcome
", }); ``` ## Postfix In `/etc/postfix/main.cf`: ``` relayhost = [smtp.splashifypro.com]:587 smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd smtp_sasl_security_options = noanonymous smtp_use_tls = yes smtp_tls_security_level = encrypt ``` In `/etc/postfix/sasl_passwd`: ``` [smtp.splashifypro.com]:587 emailapikey:pk_live_... ``` ```bash postmap /etc/postfix/sasl_passwd chmod 600 /etc/postfix/sasl_passwd* postfix reload ``` ## Supabase Auth (transactional email for signups, OTPs, password reset) Supabase ships a "Custom SMTP" panel that you can point at the relay so signup confirmation, magic-link, and password-recovery emails come from your verified sender instead of the Supabase default. **Project → Project Settings → Auth → SMTP Settings:** | Field | Value | |---|---| | Sender email | `noreply@yourcompany.com` *(must be on a verified identity)* | | Sender name | Whatever appears in the inbox From line | | Host | `smtp.splashifypro.com` | | Port | `587` | | Username | `emailapikey` | | Password | your `pk_live_…` API key | | Minimum interval per user | `60` seconds (Supabase default — anti-abuse) | Then save & hit **Send test email**. If you get `535 5.0.0 5.7.0 invalid api key format` it means you pasted something other than a `pk_live_…` (or `sk_live_…` for app-developer accounts) key — copy the key from [Settings → API key](https://email.splashifypro.com/settings) in Splashify Pro Email. ## Things to know - **Sender must be on a verified identity.** Sends from an unverified email/domain are rejected with `550 5.7.1 sender '' is not on a verified identity for this partner`. Add the domain or email at [Settings → Identities](https://email.splashifypro.com/identities) before testing. - **Suppression list applies.** Recipients on your account suppression list are rejected at RCPT TO with `550 5.7.1 recipient on suppression list`. - **Free sandbox: 200 emails/day.** New accounts are sandboxed. Once you exceed the 200/day quota you'll get `452 4.7.0 sandbox daily quota of 200 emails reached`. Request production access from the dashboard to lift it. - **Billing.** ₹0.03/email for paid sends (post-sandbox); sandbox sends are free. SMTP and REST sends bill identically. - **Webhooks fire normally.** SMTP and REST sends produce the same event stream into your configured destinations. - **TLS is required.** Plain-text AUTH is rejected; the relay only advertises AUTH after STARTTLS (port 587) or on the implicit-TLS port (465). - **Per-message metadata via headers.** Set `X-Configuration-Set:Hi
", "text_body": "Hi", "configuration_set_name": "production", "category": "transactional" } ``` Response: `{"success":true,"results":[{"recipient":"user@example.com","message_id":"", message: ""}` with a 4xx or 5xx status:
- `400` — bad request (read `message`)
- `401` — invalid / missing API key
- `402` — wallet balance insufficient (recharge)
- `403` — sandbox cap, sending paused, IP blocked
- `404` — not found
- `429` — rate limit (back off)
## Where to read more
- Full API reference: https://email-docs.splashifypro.com/api-reference
- Webhooks: https://email-docs.splashifypro.com/webhooks
- Deliverability: https://email-docs.splashifypro.com/deliverability
- Knowledge base: https://email-docs.splashifypro.com/concepts
That's everything an AI agent needs to start integrating. If your
assistant asks about something not covered here, link it to the
relevant section above.
================================================================================
# MCP Server
Section: Guides › Build with AI
URL: https://email-docs.splashifypro.com/build-with-ai/mcp-server
================================================================================
> Connect Claude, Cursor, and other AI assistants to the Splashify Pro Email API via Model Context Protocol.
# MCP Server
The **Splashify Pro MCP server** lets your AI assistant (Claude
Desktop, Cursor, Windsurf, Claude Code, etc.) call the Email API
directly. Send emails, verify identities, configure webhooks — all
from a chat conversation.
[Model Context Protocol](https://modelcontextprotocol.io) is the
open standard Anthropic shipped for connecting AI tools to external
APIs. Splashify Pro hosts an MCP server that exposes every public
endpoint as a tool the assistant can call.
## Endpoint
The MCP server is **hosted** — there's nothing to install locally.
Point your AI client at the SSE endpoint and authenticate with your
Email API key:
```
URL: https://mcp.splashifypro.com/sse
Auth: Authorization: Bearer pk_live_...
```
Use the same `pk_live_…` key you use for the REST API (generate one
at [email.splashifypro.com](https://email.splashifypro.com) →
Settings → API Key). The MCP server uses the same auth chain,
permission scope, and rate-limit budget as REST.
## Add to Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json`
(macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"splashifypro": {
"type": "sse",
"url": "https://mcp.splashifypro.com/sse",
"headers": {
"Authorization": "Bearer pk_live_..."
}
}
}
}
```
Restart Claude Desktop. You'll see "splashifypro" in the bottom-left
tools menu.
## Add to Cursor
Edit `.cursor/mcp.json` in your project root:
```json
{
"mcpServers": {
"splashifypro": {
"type": "sse",
"url": "https://mcp.splashifypro.com/sse",
"headers": {
"Authorization": "Bearer pk_live_..."
}
}
}
}
```
## Add to Claude Code
Edit `~/.claude/settings.json` or your project's `.mcp.json`:
```json
{
"mcpServers": {
"splashifypro": {
"type": "sse",
"url": "https://mcp.splashifypro.com/sse",
"headers": {
"Authorization": "Bearer pk_live_..."
}
}
}
}
```
## Add to Windsurf
Edit `~/.codeium/windsurf/mcp_config.json`. Same JSON shape as
Cursor.
## Available tools
The MCP server exposes 35+ tools, one per Email API endpoint:
| Category | Tools |
|---|---|
| Send | `partner_email_send`, `partner_email_send_template`, `partner_email_send_bulk`, `partner_email_send_raw`, `partner_email_get_status` |
| Identities | `partner_email_list_identities`, `partner_email_create_identity`, `partner_email_get_identity`, `partner_email_verify_identity`, `partner_email_delete_identity` |
| Configuration sets | `partner_email_list_configuration_sets`, `partner_email_create_configuration_set`, `partner_email_get_configuration_set`, `partner_email_delete_configuration_set` |
| Event destinations | `partner_email_create_event_destination` |
| Templates | `partner_email_list_templates`, `partner_email_create_template`, `partner_email_delete_template`, `partner_email_preview_template` |
| Suppression | `partner_email_list_suppression`, `partner_email_add_suppression`, `partner_email_remove_suppression` |
| Stats + reputation | `partner_email_get_quotas`, `partner_email_get_stats`, `partner_email_get_reputation`, `partner_email_list_events`, `partner_email_get_bounce_report` |
| Production access | `partner_email_list_production_access_requests`, `partner_email_submit_production_access` |
| Dedicated IP | `partner_email_get_dedicated_ip_request`, `partner_email_request_dedicated_ip`, `partner_email_cancel_dedicated_ip_request` |
| Account + security | `partner_email_list_activity_logs`, `partner_email_list_ip_allowlist`, `partner_email_add_ip_allowlist_entry`, `partner_email_remove_ip_allowlist_entry` |
## Example prompts
> "Send a welcome email to alex@example.com from hello@mycompany.com"
The assistant calls `partner_email_send` with the right shape, hands
the `message_id` back to you, and offers to follow up via
`partner_email_get_status`.
> "Verify the domain mycompany.com and tell me which DNS records I need"
`partner_email_create_identity` is called, the response includes the
SPF / DKIM / DMARC records, and the assistant explains where to
publish them.
> "Why are 5% of my emails bouncing?"
`partner_email_get_reputation` + `partner_email_list_events?event_type=bounce`
fire in parallel; the assistant correlates the bounces against
recipients and suggests fixes.
## Security
- **Hosted, not local.** Your API key never leaves your machine
except as a Bearer header on outbound HTTPS requests to
`mcp.splashifypro.com` — same path your REST calls already take.
- **Mutating tools confirm first.** Tools that change state
(`partner_email_send`, `partner_email_create_*`,
`partner_email_delete_*`) generally require the assistant to
read back the action; you approve before they fire.
- **Audit log.** Every MCP-driven call shows up at
[email.splashifypro.com](https://email.splashifypro.com) →
Activity Log alongside REST + SDK calls so you can see who/what
fired which endpoint when.
- **Rate limits.** MCP calls share the same per-key budget as REST.
Burst protection kicks in identically.
## Troubleshooting
- **"splashifypro server not found":** check the JSON config path +
restart the host app. SSE transport requires the host app to
support remote MCP (Claude Desktop ≥ 0.7, Cursor ≥ 0.42, Claude
Code ≥ 1.0).
- **`401 invalid_key`:** API key is wrong or revoked. Generate a
fresh one in Splashify Pro Email.
- **`402 insufficient_balance`:** wallet is empty. Recharge from
[Splashify Pro Email](https://email.splashifypro.com/wallet).
- **`429 rate_limited`:** assistant is firing too many requests
too fast — back off or apply for production access to lift the
per-second cap.
## Feedback + bugs
Spotted a bug or want a tool that isn't listed? Email
**support@splashifypro.in** with the prompt that triggered it and
your `pk_live_` key prefix (first 12 chars). We ship MCP fixes
weekly.
================================================================================
# SDKs
Section: Guides › Build with AI
URL: https://email-docs.splashifypro.com/build-with-ai/sdks
================================================================================
> Official SDKs for Node.js, Python, and PHP — auto-generated from our OpenAPI spec.
# SDKs
Official SDKs ship for **Node.js**, **Python**, and **PHP**. All
three are auto-generated from the same OpenAPI spec the API runs
against, so they stay in lock-step with the platform.
| Language | Package | Source |
|---|---|---|
| Node.js | `@splashifypro/sdk` | [github.com/splashifypro/sdk-node](https://github.com/splashifypro/sdk-node) |
| Python | `splashifypro` | [github.com/splashifypro/sdk-python](https://github.com/splashifypro/sdk-python) |
| PHP | `splashifypro/sdk` | [github.com/splashifypro/sdk-php](https://github.com/splashifypro/sdk-php) |
Don't see your language? The API is plain HTTP/JSON — every
endpoint can be called with the language's stdlib HTTP client. See
the per-language [Quickstarts](/getting-started/node-quickstart) for
hand-rolled examples.
## Node.js
```bash
npm install @splashifypro/sdk
```
```js
import { SplashifyClient } from "@splashifypro/sdk";
const client = new SplashifyClient({
apiKey: process.env.SPLASHIFY_API_KEY,
});
const result = await client.emails.send({
from: "hello@yourcompany.com",
to: ["customer@example.com"],
subject: "Welcome",
htmlBody: "Welcome
",
});
console.log(result.results[0].messageId);
```
TypeScript types ship with the package. Edge runtimes (Vercel,
Cloudflare Workers, Deno) are supported.
## Python
```bash
pip install splashifypro
```
```python
from splashifypro import SplashifyClient
client = SplashifyClient(api_key=os.environ["SPLASHIFY_API_KEY"])
result = client.emails.send(
from_="hello@yourcompany.com",
to=["customer@example.com"],
subject="Welcome",
html_body="Welcome
",
)
print(result["results"][0]["message_id"])
```
Async client also available:
```python
from splashifypro import AsyncSplashifyClient
async with AsyncSplashifyClient(api_key=...) as client:
result = await client.emails.send(...)
```
## PHP
```bash
composer require splashifypro/sdk
```
```php
use Splashifypro\Client;
$client = new Client(getenv('SPLASHIFY_API_KEY'));
$result = $client->emails->send([
'from' => 'hello@yourcompany.com',
'to' => ['customer@example.com'],
'subject' => 'Welcome',
'html_body' => 'Welcome
',
]);
echo $result['results'][0]['message_id'];
```
Laravel users can pull in our facade by registering the package's
service provider — see the README on
[github.com/splashifypro/sdk-php](https://github.com/splashifypro/sdk-php).
## Versioning
SDKs follow [semantic versioning](https://semver.org). Major version
bumps only happen for breaking changes; the API itself is versioned
under `/api/v1/` and we'll ship `/api/v2/` before any breaking
contract change so the SDK can support both.
## Reporting issues
Each SDK lives in its own repo and accepts issues + PRs:
- [github.com/splashifypro/sdk-node/issues](https://github.com/splashifypro/sdk-node/issues)
- [github.com/splashifypro/sdk-python/issues](https://github.com/splashifypro/sdk-python/issues)
- [github.com/splashifypro/sdk-php/issues](https://github.com/splashifypro/sdk-php/issues)
For platform-side bugs (the API misbehaving regardless of SDK), or
for anything you can't reduce to a single language, email
**support@splashifypro.in** with the request payload + the
`pk_live_` key prefix (first 12 chars).
================================================================================
# Common Recipes
Section: Guides › Build with AI
URL: https://email-docs.splashifypro.com/build-with-ai/recipes
================================================================================
> Curated patterns for shipping email features fast.
# 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`:
```js
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`:
```js
await client.emails.sendBulk({
from: "hello@yourcompany.com",
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 "` and
recipients see "Display Name" in their inbox:
```json
{
"from": "Sarah from Acme ",
...
}
```
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 `bounces@mail.splashifypro.com` 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:
```js
await client.emails.send({
from: "alerts@yourcompany.com",
to: ["customer@example.com"],
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:
```js
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:
```js
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:
```js
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.
================================================================================
# Knowledge Base
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base
================================================================================
> Concepts, deliverability guides, pricing, errors, and FAQ.
# Knowledge Base
Long-form guides + reference material that goes deeper than the
endpoint docs. Read these when you want to understand WHY the API
works the way it does — or when you're debugging deliverability.
## Topics
- [**Concepts**](/knowledge-base/concepts) — sending identities,
configuration sets, suppression lists, sandbox vs production,
reputation
- [**Deliverability**](/knowledge-base/deliverability) — SPF / DKIM
/ DMARC, IP warm-up, content best practices, sender reputation
- [**Pricing**](/knowledge-base/pricing): flat ₹0.03 per email,
sandbox free tier, billing model
- [**Errors**](/knowledge-base/errors) — every error code, what it
means, how to fix it
- [**FAQ**](/knowledge-base/faq) — common questions
## Quick orientation
If you're new, read these in order:
1. [Sending identities](/knowledge-base/concepts/sending-identities)
2. [Configuration sets](/knowledge-base/concepts/configuration-sets)
3. [Sandbox vs production](/knowledge-base/concepts/sandbox)
4. [SPF / DKIM / DMARC](/knowledge-base/deliverability/spf-dkim-dmarc)
5. [Pricing](/knowledge-base/pricing)
================================================================================
# Deliverability
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base/deliverability
================================================================================
> Get your email into the inbox — SPF, DKIM, DMARC, content best practices, sender reputation.
# Deliverability
Sending email isn't enough. **Inboxing** is.
Mailbox providers (Gmail, Outlook, Yahoo, Apple Mail, etc.) decide
whether your message lands in inbox, junk, or gets refused based on
a stack of signals — domain authentication, sender reputation,
content patterns, recipient engagement.
This page is the field guide.
## The 90% rule
Most deliverability problems come from one of three causes:
1. **Authentication is broken** — SPF/DKIM/DMARC misconfigured →
provider can't verify the sender → marks as spam.
2. **List quality is bad** — too many bounces (sending to dead
addresses) or too many complaints (recipients didn't expect
the email).
3. **Content tripped a spam filter** — links in the body to
blocklisted domains, misleading subject lines, missing
plaintext alternative.
Get authentication right + send to opt-in lists + don't write spammy
copy and you'll inbox.
## SPF / DKIM / DMARC
The three DNS-level authentication mechanisms every modern inbox
provider expects.
### SPF (Sender Policy Framework)
A TXT record on your domain that lists IPs / providers authorized
to send mail "from" your domain. We give you:
```
TXT yourcompany.com "v=spf1 include:_spf.mail.splashifypro.com ~all"
```
The `include:` mechanism delegates SPF lookup to our published
record, so when our IPs change you don't have to update yours.
### DKIM (DomainKeys Identified Mail)
A cryptographic signature in the email header that lets the
receiver verify the message wasn't tampered with in transit. We
publish the public key under our domain and you publish a CNAME
that points at it:
```
CNAME splashify._domainkey.yourcompany.com splashify._domainkey.mail.splashifypro.com
```
This way you don't manage private keys + we can rotate the key
periodically without coordinating with every customer.
### DMARC (Domain-based Message Authentication, Reporting & Conformance)
DMARC sits on top of SPF + DKIM. It tells the receiver what to do
with unauthenticated mail claiming to be from your domain:
```
TXT _dmarc.yourcompany.com "v=DMARC1; p=quarantine; rua=mailto:dmarc@splashifypro.com"
```
| Policy | Behaviour |
|---|---|
| `p=none` | Monitor only — receivers report but don't act |
| `p=quarantine` | Send to spam (recommended) |
| `p=reject` | Refuse outright (strongest, but risky if any legitimate sender isn't authenticated) |
The `rua=` address receives aggregate reports. We provide a public
endpoint at `dmarc@splashifypro.com` so you don't need to set up
your own.
### Verify all three pass
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Response includes per-record `pass: true/false`:
```json
{
"dns_records": {
"spf": { "pass": true, "found": "v=spf1 include:_spf.mail.splashifypro.com ~all" },
"dkim": { "pass": true, "found": "CNAME splashify._domainkey.mail.splashifypro.com" },
"dmarc": { "pass": true, "found": "v=DMARC1; p=quarantine; rua=mailto:..." }
}
}
```
## List hygiene
Bounces and complaints are the #2 cause of deliverability decline.
### Acquire opt-in addresses only
Single opt-in (email entered on a form, no confirmation step) is
the minimum. Double opt-in (form + confirmation email + click
the link) is the gold standard — drops bounce rates ~10x.
Don't:
- Buy lists
- Scrape websites
- Import a list of "all my contacts" from a co-founder's old job
- Email people who gave you their card at a conference (without
saying "we'll add you to our newsletter")
### Bounce + complaint handling
Both are auto-managed by the [suppression list](/knowledge-base/concepts/suppression).
Hard bounces and complaints get added immediately, and we never
attempt to send to suppressed addresses.
Sync to your application:
- `Bounce` webhook event → mark user `email_bounced=true` in your DB
- `Complaint` webhook event → mark user `email_complained=true` in your DB
Skip these users from any future outbound — your suppression list
is mirrored at our level, but having it locally lets you also skip
them from marketing campaigns + product onboarding flows.
### Engagement-based pruning
Mailbox providers weight engagement heavily. If a recipient hasn't
opened or clicked any of your emails in 6 months, sending to them
hurts your reputation. Drop them from active campaigns until they
re-engage (e.g. via a "we miss you" prompt).
## Content best practices
| Do | Don't |
|---|---|
| Plain `From:` (`hello@yourcompany.com`) | Display-name-only fakery (`Hello `) |
| Clear subject (no `RE:` / `FWD:` if not actually a reply) | All-caps, all-emojis, money symbols |
| Both HTML + plaintext body | HTML only — spam filters penalize |
| Unsubscribe link in marketing email | Marketing email without unsubscribe (illegal in most jurisdictions) |
| Image alt text | Image-only emails (heavy spam signal) |
| Concise body | 50% link / 50% text ratio |
We auto-generate plaintext alternative from your HTML if you don't
provide one. We also auto-inject the unsubscribe link if your
template doesn't include one (CAN-SPAM compliance).
## Sender reputation
We track [reputation](/knowledge-base/concepts/reputation) at the
account level. Bounce rate + complaint rate over rolling 14 days.
Above-threshold rates trigger automatic warnings + eventual
auto-pause.
The platform's sending infrastructure is well-warmed and has good
standing across major mailbox providers. You inherit that
reputation when you start sending — but bad behaviour from your
account hurts the broader platform reputation, which is why we're
strict about list quality.
## Subdomain strategy
For organisations sending high volume of mixed mail types, use
subdomains to isolate reputation:
| Domain | Use |
|---|---|
| `mail.acme.com` | Transactional (signup, receipts, password reset) |
| `news.acme.com` | Marketing campaigns / newsletters |
| `notify.acme.com` | App notifications (high frequency, low engagement) |
Each gets its own verified identity. Bounces / complaints on
`news.acme.com` don't drag down `mail.acme.com`'s reputation.
## Read more
- [SPF / DKIM / DMARC explainer](/knowledge-base/concepts/sending-identities)
- [Reputation thresholds + auto-pause](/knowledge-base/concepts/reputation)
- [Suppression list mechanics](/knowledge-base/concepts/suppression)
================================================================================
# Pricing
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base/pricing
================================================================================
> Flat ₹0.03 per email. No tiers, no commitments, sandbox is free.
# Pricing
We keep it simple: one rate for everyone, sandbox free, prepaid
wallet, no monthly minimums.
## Headline
| Tier | Rate |
|---|---|
| **Sandbox (default for new accounts)** | First 200/day **free**, then ₹0.03/email |
| **Production** | ₹0.03/email from email #1 |
That's it. No marketing-vs-transactional split. No volume tiers
that kick in at obscure thresholds. Same price for HTML, plaintext,
template, raw MIME, send-bulk, all of it.
## Currency + GST
Pricing is in **INR**. Indian customers are billed with 18% GST on
top. Non-India customers are billed in USD the same way, with no GST applied.
## Wallet model
The Splashify Pro Email API runs on a **prepaid wallet**:
1. Recharge your wallet in Splashify Pro Email
(`email.splashifypro.com` → Wallet → Recharge)
2. Each successful send deducts ₹0.03 (or your override rate) from
the wallet balance
3. When balance hits zero, `/send` returns
`402 INSUFFICIENT_BALANCE` until you recharge
Pay by UPI, card, netbanking or wallet. You get a GST invoice for every recharge,
emailed automatically and downloadable from the panel.
## What counts as a billable send
A send is billable when:
- The API call validates successfully
- The recipient is NOT on your suppression list
- We attempt SMTP delivery (250 OK or any 4xx/5xx response)
NOT billable:
- API rejections (`400 INVALID_REQUEST`, `400 FROM_NOT_VERIFIED`)
- Suppression-list rejections (`status: rejected` in send response)
- Sandbox sends within the daily 200 free quota
- Rate-limit rejections (`429`)
## Volume discounts
Default is one rate. For customers committing to high steady-state
volume, custom rates are negotiable. Reach out via the support
form on the panel.
Our team can set custom marketing and transactional rates on your
account. Once set, those
rates take precedence over the default ₹0.03.
## Tracking spend
Every send writes a row to your wallet billing log, which you can see at:
```
email.splashifypro.com → Wallet → Transaction history
```
Each row shows the recipient, message_id, rate at send-time, and
balance before/after. Same data is exposed via API:
```bash
curl 'https://api.splashifypro.com/api/v1/wallet/transactions/$PARTNER_ID' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Recharge minimums
Minimum recharge is ₹500. Wallet balance can drop below ₹500
between recharges. We don't gate sends on balance until it hits
exactly zero.
## Dedicated IP
For customers sending high steady-state volume (typically 10K+/day),
a dedicated sending IP isolates your reputation from the shared
pool. Request one in Splashify Pro Email under **Dedicated IP**.
| Item | Rate |
|---|---|
| **Dedicated IP** | **₹350 / month per IP** |
Charged on a monthly recurring invoice from the date the IP is
assigned. Per-email send pricing (₹0.03) does not change: a dedicated
IP is only an add-on. Cancel anytime;
billing stops at the end of the current billing month and the IP
is returned to the shared pool.
## Postpaid (legacy)
If you're an older customer on a postpaid wallet (`wallet=Post Paid`),
sends accrue against your `outstanding` balance up to your
`credit_limit`. Settled monthly with an invoice. Newer
accounts ship as prepaid by default. Postpaid is grandfathered for
existing customers.
## What we don't charge for
- Verifying identities
- Creating configuration sets / templates / event destinations
- Webhook deliveries to your endpoint
- Suppression list management
- Reading stats / quotas / events
- API rate limit when you stay under the per-second peak
The wallet only deducts on **actual sends**.
## Invoices + GST documents
Every recharge generates:
- A GST invoice (PDF, downloadable from Splashify Pro Email)
- An automatic email to your account email
- The payment marked as paid once the wallet credit lands
For India-based customers with a GSTIN, the invoice carries your
GSTIN + state code, usable for input-tax-credit reconciliation.
## Refunds
- **Wallet balance** is non-refundable as cash. Once recharged, the
balance is yours to spend on email sends.
- **Failed sends** (4xx/5xx) are not billed in the first place.
- **Mistaken duplicate recharges:** contact support and we'll
refund or credit the duplicate.
- **Account closure:** unused balance can be donated to a future
account with the same email or refunded under exceptional
circumstances. Contact support.
## Comparison
| Provider | Per-email rate | Free tier |
|---|---|---|
| **Splashify Pro** | ₹0.03 | 200/day in sandbox |
| AWS SES (sending alone) | ~$0.0001 (~₹0.008) | 62K/month from EC2 |
| Resend | ~$0.0001 (~₹0.008) | 100/day |
| Postmark | ~$0.0015 (~₹0.12) | 100/month |
| SendGrid | ~$0.0007–$0.001 | 100/day |
We come in cheaper than Postmark and SendGrid, with extra benefits
(panel, support, simpler onboarding) layered on top.
================================================================================
# Errors
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base/errors
================================================================================
> Every API error code, what it means, and how to fix it.
# Errors
Every error from the Email API has a stable `error` code + a
human-readable `message`. Build retry / error-handling logic against
the code, not the message.
## Response shape
```json
{
"success": false,
"error": "INSUFFICIENT_BALANCE",
"message": "Wallet balance ₹0.00. Recharge to continue sending."
}
```
`success: false` is always present. `error` is the stable code.
`message` is for display + may change for clarity over time.
## 400 Bad Request
| Code | Cause | Fix |
|---|---|---|
| `INVALID_REQUEST` | Malformed JSON or missing required field | Read `message` |
| `INVALID_AMOUNT` | Non-positive amount on a wallet recharge | Pass amount > 0 |
| `INVALID_PARAM` | Path / query param doesn't parse | Check the URL |
| `MISSING_PARAM` | Required param missing | Read `message` |
| `FROM_NOT_VERIFIED` | `from` address not on a verified identity | Verify domain via `POST /identities` |
| `BILLING_INCOMPLETE` | Account profile missing required fields | Fill `PUT /partner/self/billing` |
| `BILLING_NOT_SET_UP` | Zoho customer not yet created | Save billing once via `PUT /partner/self/billing` |
| `INVALID_SIGNATURE` | HMAC check on payment-verify failed | Don't tamper with payment widget output |
| `BAD_RECIPIENT` | Recipient address malformed | Validate before send |
| `TOO_MANY_RECIPIENTS` | Per-message recipient cap exceeded (50) | Split into multiple sends |
## 401 Unauthorized
| Code | Cause | Fix |
|---|---|---|
| `MISSING_AUTH` | No `Authorization` header | Add `Authorization: Bearer pk_live_...` |
| `INVALID_KEY_FORMAT` | Key doesn't match `pk_live_...` | Regenerate in Splashify Pro Email |
| `KEY_NOT_FOUND` | Key was revoked or never existed | Regenerate |
| `KEY_INACTIVE` | Account suspended | Contact support |
## 402 Payment Required
| Code | Cause | Fix |
|---|---|---|
| `INSUFFICIENT_BALANCE` | Wallet balance < send price | Recharge wallet |
## 403 Forbidden
| Code | Cause | Fix |
|---|---|---|
| `IP_BLOCKED` | Your IP isn't on your account's IP allowlist | Add to allowlist or call from an allowed IP |
| `SENDING_PAUSED` | Account paused (admin OR reputation auto-pause) | Contact support / clean lists |
| `SANDBOX_QUOTA_REACHED` | Daily 200/day sandbox cap hit | Request production access |
| `FEATURE_LOCKED` | Plan doesn't include this feature | Upgrade plan |
## 404 Not Found
| Code | Cause | Fix |
|---|---|---|
| `PARTNER_NOT_FOUND` | Account ID doesn't exist | Check the ID |
| `IDENTITY_NOT_FOUND` | Identity hasn't been created | Create via `POST /identities` |
| `CONFIG_SET_NOT_FOUND` | Configuration set name unknown | Check name spelling |
| `TEMPLATE_NOT_FOUND` | Template name not registered | Create via `POST /templates` |
| `MESSAGE_NOT_FOUND` | message_id doesn't match any send | Check the ID + day_bucket |
## 409 Conflict
| Code | Cause | Fix |
|---|---|---|
| `EMAIL_ALREADY_REGISTERED` | Signup with already-used email | Use forgot-password to recover |
| `IDENTITY_EXISTS` | Already verified — duplicate POST | Idempotent — existing row returned |
| `CONFIG_SET_NAME_TAKEN` | Another set with that name exists | Pick a unique name |
## 429 Too Many Requests
| Code | Cause | Fix |
|---|---|---|
| `RATE_LIMITED` | Per-second send rate exceeded | Backoff and retry |
| `OTP_RATE_LIMITED` | OTP resend before cooldown | Wait 60 seconds |
## 500 Internal Server Error
| Code | Cause | Fix |
|---|---|---|
| `DB_ERROR` | Database query failed | Retry with backoff — usually transient |
| `INTERNAL_ERROR` | Catch-all | Retry once; if persistent, contact support |
| `PAYMENT_GATEWAY_ERROR` | Zoho upstream returned 5xx | Retry — usually transient |
## 502 Bad Gateway
| Code | Cause | Fix |
|---|---|---|
| `PAYMENT_GATEWAY_ERROR` | Zoho returned an error response | Wait + retry; check Zoho status |
## 503 Service Unavailable
| Code | Cause | Fix |
|---|---|---|
| `DB_UNAVAILABLE` | Database connection issue | Retry — should clear within seconds |
## SMTP errors (in webhook payloads)
When a `Bounce` event fires, the `bounce.bouncedRecipients[].diagnosticCode`
field carries the SMTP diagnostic from the recipient's MX. Common ones:
| Code | Meaning |
|---|---|
| `550 5.1.1 user unknown` | Recipient doesn't exist (hard bounce) |
| `550 5.1.10 No such user` | Same — different MX wording |
| `550 5.7.1 spam policy` | Receiver classified as spam (treat as complaint) |
| `552 5.2.2 mailbox full` | Soft bounce — retry |
| `421 4.7.0 try again later` | Greylisting / load — retry |
| `554 5.7.1 sender rejected` | Sender domain blocked by recipient — list cleaning needed |
## Retry strategy
Rule of thumb:
- **400-class errors:** don't retry. Fix the request.
- **402:** don't retry. Recharge first.
- **403 + 404:** don't retry. Fix configuration first.
- **429:** retry with exponential backoff (start 1s, double up to 60s).
- **500 / 502 / 503:** retry up to 3 times with exponential backoff.
Idempotency: `/send` is NOT idempotent on retry — duplicate calls
with the same body will produce duplicate email sends. Implement
your own dedup against your business identifier (charge_id, etc.)
before calling `/send`.
## When to contact support
- 500-class errors persist > 5 minutes
- 402 even though wallet balance shows positive
- 403 SENDING_PAUSED with no clear cause in `/reputation`
Email support@splashifypro.in or open a ticket in Splashify Pro
Email.
================================================================================
# FAQ
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base/faq
================================================================================
> Common questions about the Splashify Pro Email API.
# FAQ
## Account & Billing
### How long does production access take?
Most requests are reviewed within 24 business hours. Clean
applications (verified domain, real use case, no obvious red flags)
are typically approved within an hour.
### Can I have multiple API keys?
Yes — generate as many as you need from **Settings → API Keys**.
Common pattern: one per environment (staging/production), one per
service (web app / cron worker / etc). Revoke individually without
affecting the others.
### Is there a free tier?
Sandbox accounts get 200 emails/day free. After production access,
you pay ₹0.03/email from email #1. There's no monthly minimum or
commitment.
### How do I close my account?
Email support@splashifypro.in. Wallet balance can either be
transferred to a new account or refunded under exceptional
circumstances.
## Sending
### Can I send from a Gmail address?
No. We refuse to verify public-domain mailboxes (gmail.com,
yahoo.com, outlook.com, etc.) — sending bulk through them violates
the providers' TOS and would get our IP blocklisted. Use your own
domain.
### Can I send to anyone?
In **production**, yes. In **sandbox**, only to verified-recipient
addresses (email-address identities you've explicitly added).
### What's the maximum recipients per send?
50 per `/send` request. For larger lists, use `/send-bulk` (50
destinations × 50 recipients = 500 max per request) or a
campaign-style fan-out.
### What's the maximum email size?
10 MB total (HTML + text + attachments). The MIME-encoded message
counts toward this limit.
### Can I attach files?
Not via `/send` directly today. `/send-raw` accepts a complete MIME
message (base64-encoded) — you build the multipart structure
yourself. Attachment-aware send is on the roadmap.
### Are there inline images?
With `/send-raw` you control the entire MIME tree, including
inline `Content-ID:` references. With `/send` use externally-hosted
images (e.g. on a CDN) — most modern email clients strip `cid:`
references when forwarded anyway.
## Domain & Identity
### Why do I need to verify a domain?
Without verification, anyone could claim to send from your domain
through our infrastructure. Verification proves ownership and is
required by every modern ESP for the same reason.
### How long does DNS verification take?
After publishing the records, typically 5-15 minutes. Some DNS
providers cache aggressively (Cloudflare flattening, GoDaddy
24-hour TTL) — call `POST /verify` after publishing and check
`status`.
### Can I use a subdomain?
Yes — `mail.acme.com` is a valid identity. Common pattern:
- `mail.acme.com` for transactional sends
- `news.acme.com` for marketing
This isolates reputation between mail types.
### What if my DKIM CNAME is flattened by my DNS provider?
Cloudflare's CNAME flattening serves the resolved TXT record
directly. We accept either CNAME OR TXT for the DKIM check —
flattened-but-correct records pass.
## Webhooks
### How fast do webhooks fire after a send?
Send + Reject webhooks fire within ~1 second of the API response.
Delivery webhooks fire within ~5-30 seconds (the SMTP attempt time).
Bounce webhooks fire within seconds (sync hard bounces) or minutes
to hours (async DSN bounces).
### What if my webhook endpoint is down?
We retry on 5xx + timeout per the [retry schedule](/webhooks/retries)
(1min, 5min, 15min). After ~21 minutes total we give up. 4xx
responses (auth, schema mismatch) get NO retry — your URL is broken.
### Can I have multiple webhook destinations?
Yes — up to 5 per configuration set. Useful for routing engagement
events to one URL and deliverability events to another.
### How do I rotate the webhook secret?
PATCH the destination with a new `webhook_secret`. The API never
echoes the new value back, so save it locally before the next
event fires. Brief overlap window is your responsibility — accept
both old + new for ~30 seconds during rotation.
## Pricing
### Is the rate marketing-vs-transactional?
No. Flat ₹0.03 per email regardless of category. Simpler for both
sides.
### Are SMS credits separate?
Yes — SMS is a separate product with its own billing. This API is
email-only.
### Do I get a tax invoice?
Yes. Every wallet recharge generates a Zoho Billing invoice with
GST (for India-based customers with GSTIN). Downloadable from
Splashify Pro Email.
## Compliance
### Is this CAN-SPAM compliant?
The API supports compliance — auto-injected Unsubscribe headers
(RFC 8058 one-click), suppression list, no anonymous sending. But
compliance is YOUR responsibility — you must ensure recipients
opted in, your unsubscribe link works, and your physical mailing
address appears in the body.
### GDPR / DPDP?
Same answer — we provide the tools (suppression list for erasure
requests, audit logs for data subject access requests). You are
the data controller; we're the processor.
### What about CASL (Canada)?
Same model. Express consent + clear identification + working
unsubscribe + 10-day honor window. We honor the platform-level
suppression list immediately, but you must process unsubscribe
clicks via webhook and update your own user state.
## Migration
### I'm coming from AWS SES — how compatible is the API?
Endpoint shapes and response field names map 1:1 in most cases.
The webhook event payload shape matches AWS SES SNS event
publishing exactly. SDKs feel similar (resource → action). Most
migrations are a base-URL swap + an auth header swap.
### From Resend / Postmark / SendGrid?
Similar shapes. The biggest difference is the [configuration set
+ event destination model](/knowledge-base/concepts/configuration-sets)
which mirrors AWS SES rather than the per-message metadata model
some providers use. If you used SES configuration sets, you're
home.
### Can I forward existing webhooks?
We don't accept incoming webhooks (we deliver them, we don't
receive). For event-source migration, set up a webhook destination
on a new config set and migrate sends to use it.
## Support
### Where do I get help?
- **Maya AI assistant**: every page has the ✨ button bottom-right
- **Search** (Ctrl/⌘+K): every doc page is indexed
- **Email support**: support@splashifypro.in
- **Splashify Pro Email**: built-in support ticket flow
### Is there a status page?
Yes — [status.splashifypro.com](https://status.splashifypro.com).
We post incidents within ~5 minutes of detection.
### Where's the changelog?
Linked from the Splashify Pro Email footer. Subscribe to the RSS feed
to get notified of new endpoints + behaviour changes.
================================================================================
# Connected apps
Section: Guides › Knowledge Base
URL: https://email-docs.splashifypro.com/knowledge-base/connected-apps
================================================================================
> Connect other apps to your Splashify Pro Email account, see what each one can do, and remove or report an app.
# Connected apps
Some apps you use, like a CRM or a shop builder, can connect to your Splashify Pro Email account and send email or read reports for you. A connected app can do only what you allowed. You can see every connected app and remove any of them at any time.
## Connect an app
You start in the other app, usually with a button like **Connect Splashify Pro**. It opens a page on email.splashifypro.com that shows:
- the app's name and logo, and who made it. A **verified** app has a green **Verified** badge and the company that made it; any other app shows **by an unverified developer** and a notice that we have not checked it;
- the Splashify Pro Email account it will connect to;
- what the app will be able to do, in plain words;
- how its emails are charged;
- where you go back to after you allow.
Click **Allow** to connect, or **Cancel**. You go back to the other app either way: after **Allow** you first see **Connected**, and if the page does not take you back by itself, click **Continue**.
### Who can connect apps
Only you, signed in to the panel, can connect or remove apps. An API key cannot, and neither can our support team when they look at your account.
### What it costs
Every email a connected app sends is charged like the emails you send yourself: from your wallet at your normal rates, or to your account for postpaid accounts. An app that is not verified can send up to **500 emails a day** from your account and cannot use bulk sending. After you allow an app, we email you which app was connected and what it can do. If you did not connect it, remove it at once.
### Allowed IPs
If your account only accepts API calls from its [allowed IP addresses](https://email.splashifypro.com/allowed-ips), a connected app's requests are refused unless they come from one of them. The connect page shows the addresses the app says it uses; add them in **Allowed IPs**.
## See your connected apps
Open **Settings** and click **Connected apps**. For each app you see what it can do, when and by whom it was connected, and when it was last used.
Connecting and removing apps also show in your **Activity logs**.
## Remove an app
Click **Remove** on the app's row, then **Remove** again to confirm. The app loses access at once. Anything it already sent stays in your account.
## Report an app
If an app does something you did not expect, click **Report** on the app's row, tell us what went wrong and click **Send report**. We look into every report. Remove the app too if you do not want it to keep working.
## Build your own app
Developers build apps at [dev.splashifypro.com](https://dev.splashifypro.com). See [Connected apps (OAuth)](/api-reference/connected-apps).
================================================================================
# Concepts
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts
================================================================================
> The mental model behind the Splashify Pro Email API.
# Concepts
A short tour of the primitives the API exposes. If you've worked
with AWS SES the names map 1:1 — sending identities, configuration
sets, suppression lists, all here.
## The 5 primitives
```
┌─────────────────────────────────────────────────────────┐
│ Your Splashify Pro account │
│ │
│ ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ │
│ │ Identity │ │ Identity │ │ Identity │ │
│ │ (your domain) │ │ (alt addr) │ │ (3rd domain)│ │
│ └───────┬───────┘ └───────┬───────┘ └──────┬──────┘ │
│ │ verified │ verified │ pending │
│ ┌───────┴──────────────────┴─────────────────┴──────┐ │
│ │ Configuration Sets │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ "production" │ │ "marketing" │ │ │
│ │ │ ↓ events │ │ ↓ events │ │ │
│ │ │ → webhook A │ │ → webhook B │ │ │
│ │ │ → webhook B │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Suppression list (account-wide) │ │
│ │ bounced@example.com angry@example.com ... │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ Wallet: ₹X.XX Sandbox: ON / OFF │
└─────────────────────────────────────────────────────────┘
```
1. **[Sending identities](/knowledge-base/concepts/sending-identities)** —
verified domains + email addresses you can send `From:`.
2. **[Configuration sets](/knowledge-base/concepts/configuration-sets)** —
logical groupings of sends that route events to webhook
destinations.
3. **[Suppression list](/knowledge-base/concepts/suppression)** —
account-wide blocklist. Auto-populated by hard bounces +
complaints + unsubscribes.
4. **[Reputation](/knowledge-base/concepts/reputation)** — your
account's bounce + complaint rate. Drives auto-pausing if
thresholds breach.
5. **[Sandbox vs production](/knowledge-base/concepts/sandbox)** —
sandbox = 200/day cap + verified-only recipients. Production =
real volume after a quick review.
## Hierarchy
- **Identity** is account-wide. Verifying `acme.com` lets you send
from any address at `acme.com`.
- **Configuration set** scopes sends — events from sends inside
a config set go to that set's webhook destinations.
- **Suppression list** is account-wide by default. Each config set
can opt to also suppress per-config-set on bounces / complaints
via `suppression_options`.
## Send-time flow
```
POST /send (with config_set + from + to)
│
▼
1. From-domain on a verified identity? ──── no → 400 FROM_NOT_VERIFIED
│ yes
▼
2. Recipient on suppression list? ──── yes → status=rejected
│ no
▼
3. Wallet balance >= ₹0.03? ──── no → 402 INSUFFICIENT_BALANCE
│ yes (or in sandbox free tier)
▼
4. Daily quota not exceeded? ──── no → 429 SANDBOX_QUOTA_REACHED
│ yes
▼
5. Queue the email for delivery
│
▼
6. Fire "Send" event → webhook destinations
│
▼
7. SMTP attempt → recipient MX
├── 250 OK → "Delivery" event
├── 5xx hard bounce → "Bounce" event + suppress
└── 4xx soft bounce → retry (5min, 30min, 2h)
```
================================================================================
# Sending Identities
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts/sending-identities
================================================================================
> Verified domains and email addresses are the foundation of every send.
# Sending Identities
A **sending identity** is anything you've proven you control:
- A **domain** (preferred: covers any address at that domain), or
- A specific **email address** (limited to that one mailbox)
Every email you send must have a `From:` address that matches a
**verified** identity. Sending from an unverified address returns
`400 FROM_NOT_VERIFIED`.
## Why verification?
Without verification, anyone could claim to send from
`security@yourbank.com` through our infrastructure. Verification
proves you control the domain or address at the DNS / mailbox level.
This is the same reason AWS SES, SendGrid, Postmark, Mailgun, and
every other ESP requires it. It's not optional in 2026's email
landscape.
## Domain verification
A domain is verified with DNS records you publish: SPF, DKIM and DMARC,
and for most domains also DKIM 2 and Return path. Once SPF, DKIM and
DMARC are published and checked, you can send from **any address** at
the domain: `hello@`, `alerts@`, `noreply@`, etc.
Create the identity:
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"identity_type": "DOMAIN", "identity_value": "yourcompany.com"}'
```
The response lists the records to publish under `dns_records`:
| Key | Record | Type | Hostname | Value |
|---|---|---|---|---|
| `spf` | SPF | TXT | `yourcompany.com` | `v=spf1 include:_spf.mail.splashifypro.com ~all` |
| `dkim` | DKIM | CNAME | `splashify._domainkey.yourcompany.com` | `splashify._domainkey.mail.splashifypro.com` |
| `dmarc` | DMARC | TXT | `_dmarc.yourcompany.com` | `v=DMARC1; p=quarantine; rua=mailto:dmarc@splashifypro.com` |
| `dkim2` | DKIM 2 | TXT | made for your domain | made for your domain |
| `return_path` | Return path | CNAME | made for your domain | made for your domain |
What the last two do:
- **DKIM 2** is a second DKIM key. It lets inbox providers verify mail
from your domain on all our sending servers.
- **Return path** lets bounced mail come back to us so your domain keeps
a clean sending record.
Add all five. You can send as soon as the first three are verified; add
the other two for the best delivery. The identity `status` does not wait
for them. If a domain lists only `spf`, `dkim` and `dmarc`, publish those
three.
Each entry of `dns_records` has the same shape:
```json
{
"dns_records": {
"spf": { "type": "TXT", "hostname": "yourcompany.com", "value": "v=spf1 include:_spf.mail.splashifypro.com ~all", "pass": false },
"dkim": { "type": "CNAME", "hostname": "splashify._domainkey.yourcompany.com", "value": "splashify._domainkey.mail.splashifypro.com", "pass": false },
"dmarc": { "type": "TXT", "hostname": "_dmarc.yourcompany.com", "value": "v=DMARC1; p=quarantine; rua=mailto:dmarc@splashifypro.com", "pass": false },
"dkim2": { "type": "TXT", "hostname": "...", "value": "...", "pass": false },
"return_path": { "type": "CNAME", "hostname": "...", "value": "...", "pass": false }
},
"dkim2_ok": false,
"return_path_ok": false
}
```
When `dkim2` and `return_path` are listed, the identity also carries
`dkim2_ok` and `return_path_ok` next to `spf_ok`, `dkim_ok` and
`dmarc_ok`. They can show up a moment after you create the identity: if
the create response lists only three records, read the identity again
with `GET /partner/email/identities` or open the **Sending Identities**
page in Splashify Pro Email. Always copy the hostname and value of
`dkim2` and `return_path` from there; they are made for your domain.
After publishing on your DNS provider, trigger a re-check:
```bash
curl -X POST https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com/verify \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
The same call re-checks all the records. Once `"status": "VERIFIED"`
lands, you're good. When `dkim2_ok` or `return_path_ok` in that response
is still `false`, that record is pending: add it for the best delivery.
We re-check verified domains every 24 hours; if the SPF, DKIM or DMARC
record disappears, status flips back to `PENDING` and we email you.
## Email-address verification
For low-volume use cases or when you can't add DNS records (you're
using a public-domain mailbox like Gmail), verify a single address:
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"identity_type": "EMAIL_ADDRESS", "identity_value": "alerts@yourcompany.com"}'
```
We email a verification link to the address. Click it (or use the
verify endpoint directly with the included token) and the address
becomes verified.
> **Public-domain caveat:** We refuse to verify addresses at common
> public providers (`gmail.com`, `yahoo.com`, `outlook.com`, etc.).
> Sending bulk through `you@gmail.com` violates Google's TOS and
> would get our IP blocklisted within hours. Use your own domain.
## Display name vs verified address
You can set a friendly display name without verifying it:
```json
{
"from": "Sarah from Acme "
}
```
`acme.com` must be verified. `Sarah from Acme` is unverified:
recipients see whatever string you supply.
## Multiple domains
Verify as many domains as you like. Common pattern: one for
transactional (`mail.acme.com`), one for marketing (`news.acme.com`).
Helps deliverability: bounces / complaints on the marketing domain
don't drag down transactional reputation.
## Listing your identities
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Removing an identity
```bash
curl -X DELETE https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
In-flight sends from that domain complete normally; new sends are
rejected with `FROM_NOT_VERIFIED`.
## Auto-recheck behaviour
Verified domains: re-checked every 24 hours.
Pending domains: re-checked every 1 hour for 7 days, then drop to
`FAILED` if SPF, DKIM and DMARC still aren't published.
You can always re-trigger via `POST /verify`.
## Read more
- [SPF / DKIM / DMARC explainer](/knowledge-base/deliverability/spf-dkim-dmarc)
- [API reference: identities](/api-reference/identities/create)
================================================================================
# Configuration Sets
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts/configuration-sets
================================================================================
> Logical groupings of sends — control event routing, suppression scope, and attribution.
# Configuration Sets
A **configuration set** is a logical bundle of sends that share
event-destination wiring + suppression behaviour. Modelled directly
on AWS SES configuration sets.
If you're shipping email for a single product, you might never
create a config set — defaults work fine. But the moment you have:
- **Multiple downstream tenants** (one for each customer)
- **Multiple email types** (transactional vs marketing)
- **Distinct webhook destinations** per use case
...config sets become essential.
## What a config set carries
| Field | Purpose |
|---|---|
| `name` | Human-friendly identifier you reference on `/send` |
| `description` | Free-text notes |
| `customer_id` | Optional. Your downstream end-customer attribution |
| `sending_enabled` | Kill switch — disable sends through this set without deleting it |
| `reputation_tracking_enabled` | Whether bounces/complaints from this set count toward your reputation rolling window |
| `suppression_options` | Which categories auto-add to suppression list: `NONE` / `BOUNCE` / `COMPLAINT` / `BOUNCE_AND_COMPLAINT` |
| `tags` | Arbitrary key-value pairs that ship in the webhook `mail.tags` field |
## Create one
```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",
"description": "Production transactional sends",
"suppression_options": "BOUNCE_AND_COMPLAINT",
"reputation_tracking_enabled": true,
"tags": {
"env": "prod",
"team": "platform"
}
}'
```
## Reference on send
```json
{
"from": "alerts@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Payment received",
"html_body": "...",
"configuration_set_name": "production"
}
```
Events from this send fan out to every event destination attached
to the `production` config set.
## Multi-tenant pattern
If you're running a SaaS that sends email on behalf of customers,
create one config set per customer:
```js
// On customer signup:
const cfg = await client.configurationSets.create({
name: `customer_${customer.id}`,
customer_id: customer.id,
description: `Email sends for ${customer.name}`,
tags: { customer_id: customer.id, plan: customer.plan },
});
// On send:
await client.emails.send({
from: customer.fromAddress,
to: [recipient],
configuration_set_name: `customer_${customer.id}`,
...
});
```
Now per-customer stats roll up cleanly via:
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/stats?config_set_id=...' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
And per-customer event destinations let each customer have their
own webhook URL.
## Event destinations
A config set without event destinations still records events
internally — they show up in `GET /events` and `GET /stats` — but
nothing fans out to your servers.
Add a webhook destination:
```bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets/$CFG/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/email",
"webhook_secret": "...",
"matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"]
}'
```
Multiple destinations per set are fine — useful for routing
engagement events to one URL and deliverability events to another.
## Disabling a config set
```bash
curl -X PATCH https://api.splashifypro.com/api/v1/partner/email/configuration-sets/$CFG \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-d '{"sending_enabled": false}'
```
Sends with `configuration_set_name` matching this set get
`status: rejected` until re-enabled. In-flight sends complete.
## Suppression options
| Value | Behaviour |
|---|---|
| `NONE` | Auto-suppression OFF for this set's sends |
| `BOUNCE` | Hard bounces auto-add to suppression list |
| `COMPLAINT` | Complaints auto-add to suppression list |
| `BOUNCE_AND_COMPLAINT` | Both auto-add (default + recommended) |
Manual suppressions via `PUT /suppression/:email` always go to the
account-wide list regardless of config-set settings.
## Reputation tracking
When `reputation_tracking_enabled: false`, bounces + complaints
from this set's sends are excluded from your account's rolling
reputation calculation. Useful for:
- Test campaigns where you knowingly send to low-quality addresses
- Risk-isolated experiments
Defaults to `true`. Don't turn this off unless you really mean it.
## Limits
- 100 configuration sets per account
- 5 event destinations per configuration set
- Names are unique per account, case-sensitive, alphanumeric +
`_` + `-`, 1-256 chars
================================================================================
# Suppression List
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts/suppression
================================================================================
> Account-wide blocklist that protects your sender reputation.
# Suppression List
The suppression list is your account's blocklist of recipient
addresses we won't send to. It exists to protect:
- **Your reputation** — repeated bounces / complaints tank your
bounce + complaint rate, which mailbox providers use as a
primary signal.
- **The recipient** — opting out, manually unsubscribing, or
marking as spam should mean they never hear from you again.
- **The platform** — collectively low bounce/complaint rates keep
the platform's deliverability healthy across all senders.
## What gets auto-added
| Trigger | Reason |
|---|---|
| Hard bounce | Permanent delivery failure (no such user, mailbox terminated, etc.) |
| Complaint | Recipient marked the email as spam (FBL report from inbox provider) |
| Unsubscribe | Recipient clicked a one-click unsubscribe (RFC 8058) link |
Soft bounces (mailbox full, server temporarily unavailable, etc.)
do NOT auto-suppress — we retry up to 3 times, then mark the row
failed without suppressing.
## What we DON'T auto-add
- Single soft bounces (we retry first)
- API rejections (e.g. invalid recipient format) — those don't
reach SMTP
- Greylist 4xx responses (we retry per-domain backoff)
## Behaviour at send time
When you call `/send`, every recipient is checked against the
suppression list **before** any SMTP attempt. Recipients on the
list:
- Get `status: rejected` in the per-recipient response
- Don't burn wallet balance — you're not billed for rejected
recipients
- Generate a `Reject` webhook event with `reason: "suppressed"`
This matters at scale — sending a bulk to a list with 5%
suppressed addresses saves you 5% of the wallet hit + keeps your
bounce rate clean.
## Listing the suppression list
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/suppression?reason=BOUNCE&limit=100' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Filters: `reason` (BOUNCE / COMPLAINT / UNSUBSCRIBE / MANUAL),
`search` (substring), `limit` (1-1000).
## Adding manually
```bash
curl -X PUT https://api.splashifypro.com/api/v1/partner/email/suppression/legal-hold@example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "MANUAL", "details": "GDPR erasure request 2026-05-03"}'
```
Use cases:
- GDPR / DPDP erasure requests
- Customer-side opt-outs that didn't come through your unsubscribe
flow
- Known-bad addresses you want to skip preemptively
## Removing
```bash
curl -X DELETE https://api.splashifypro.com/api/v1/partner/email/suppression/customer@example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Removal lets you re-send to that address. Be conservative — if the
address bounced hard or complained, removing it and re-sending is
likely to get you suppressed again + drag your reputation down.
## Per-config-set suppression scope
By default, suppressions are account-wide. Each config set can
override which categories auto-suppress via the `suppression_options`
field:
| Value | What gets auto-suppressed |
|---|---|
| `NONE` | Nothing (rare — you're saying "send everything regardless") |
| `BOUNCE` | Hard bounces only |
| `COMPLAINT` | Complaints only |
| `BOUNCE_AND_COMPLAINT` | Both (recommended default) |
The check at send time still hits the account-wide list — config-
set settings only affect what's auto-WRITTEN to the list.
## Best practices
- **Don't programmatically remove suppressions in bulk.** Each
removed entry that re-bounces hurts you twice.
- **Sync to your application's user table.** Listen to `Bounce`
+ `Complaint` webhooks, mark those users `email_unsubscribed: true`
in your DB, and skip them in your own outbound logic. Defense in
depth.
- **Honor unsubscribe requests instantly.** If a customer clicks
unsubscribe in your app's preferences page, push their email to
our suppression list via PUT — don't wait for the next campaign
to filter them.
- **Audit periodically.** Run `GET /suppression?reason=BOUNCE` once
a month and cross-reference against your active customer list —
bounced addresses on your billing roster mean broken support
delivery + missed renewal emails.
================================================================================
# Reputation
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts/reputation
================================================================================
> How we track sender reputation + what triggers auto-pause.
# Reputation
Mailbox providers (Gmail, Outlook, Yahoo, Apple Mail) decide
whether to deliver your mail to inbox, junk, or refuse it
entirely based on **sender reputation** — a continuously-updated
score derived from your bounce rate, complaint rate, content
patterns, and recipient engagement.
We track two of the load-bearing signals:
- **Bounce rate** — `bounced / sent` over the rolling 14-day window
- **Complaint rate** — `complained / sent` over the rolling 14-day window
Both are computed from the per-day counter table behind
`GET /partner/email/stats`.
## Status thresholds
```
bounce > 10% OR complaint > 0.5% → PAUSED (auto-pause)
bounce > 5% OR complaint > 0.1% → AT_RISK (warning)
else → HEALTHY
```
These match AWS SES and the broader industry-standard
"watch-list" thresholds.
## What happens at each threshold
### `HEALTHY`
Default state. No special handling.
### `AT_RISK`
- Email notification to the account contact
- Banner on the Splashify Pro Email dashboard
- Recommendation to investigate recent campaigns + clean lists
- No automatic action against your sending — but you should fix the
underlying issue immediately
### `PAUSED`
- All `/send` calls return `403 SENDING_PAUSED`
- The reputation status reflects on `GET /partner/email/quotas`
- Your panel dashboard shows the alert + reason
- An admin reviews + decides whether to:
- Drop you back to sandbox (so you can test fixes)
- Re-enable with a stern warning
- Keep paused pending list-cleaning evidence
To resume sending after PAUSED:
1. Identify the breach source (which campaigns / list segments
caused it)
2. Clean the list — remove every address that bounced / complained
3. Reach out to support with your remediation plan
4. Our team reviews it and lifts the pause on your account
## What we don't track (yet)
- **Engagement signals** (open rate, reply rate, forward rate) —
on roadmap for shared-IP reputation; today these only matter for
dedicated-IP customers.
- **Spam-filter scores** (SpamAssassin etc.) — you control content;
our infrastructure handles authentication.
- **Domain age / DMARC alignment** — hardened automatically through
the verification flow.
## Per-config-set vs account-wide
By default reputation is computed at the **account level**. Sends
across all config sets contribute to the same rolling window.
If you want to isolate a high-risk experiment from your main
reputation, set `reputation_tracking_enabled: false` on that
config set. Bounces + complaints from those sends are still
recorded in your event log + suppression list, but excluded from
the rolling reputation calculation.
Use sparingly — `false` is an opt-out from a healthy default.
## Inspecting your reputation
```bash
curl https://api.splashifypro.com/api/v1/partner/email/reputation \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Response:
```json
{
"success": true,
"window_days": 14,
"sent": 12500,
"bounced": 218,
"complained": 4,
"bounce_rate": 0.01744,
"complaint_rate": 0.00032,
"status": "HEALTHY"
}
```
`/quotas` returns the same status alongside daily-quota info — use
that endpoint as the single read-once-per-page source on your
dashboard.
## How to keep reputation healthy
1. **Send to opt-in lists only.** Buying lists or scraping email
addresses is the #1 cause of complaint-rate breaches.
2. **Honor unsubscribes within 10 days.** It's a CAN-SPAM /
CASL / DPDP requirement AND it's how you avoid complaint
spikes.
3. **Verify before sending.** Hitting hundreds of stale addresses
once spikes your bounce rate hard.
4. **Warm up new identities slowly.** Mailbox providers throttle
first-contact volume — start with low volume to high-engagement
recipients, ramp over a week or two.
5. **Watch your DMARC reports.** Misaligned mail (sent through us
but with wrong From-domain) gets quarantined or rejected — that
shows up as bounces in our stats.
================================================================================
# Sandbox vs Production
Section: Guides › Knowledge Base: Concepts
URL: https://email-docs.splashifypro.com/knowledge-base/concepts/sandbox
================================================================================
> What sandbox mode means, why we ship every account in it by default, and how to leave.
# Sandbox vs Production
Every new account starts in **sandbox mode**. Sandbox is a
deliberately constrained version of production where:
| Limit | Sandbox | Production (default) |
|---|---|---|
| Daily send quota | **200 emails/day** | 50,000 emails/day |
| Peak send rate | **1 email/second** | 14 emails/second |
| Recipient restrictions | Verified-recipient addresses only (TODO; today: cap-only) | Anyone |
| Per-email price | First 200/day **free** | ₹0.03/email from email #1 |
| All other features | Identical to production | — |
Sandbox is NOT a free trial. It's a probation period — we want to
see you can verify a domain, configure webhooks, and send a few
test emails without making the platform's shared IP reputation
worse.
## Why we ship sandbox by default
In our first year of operation, ~30% of new accounts caused some
form of deliverability damage in week 1. Reasons:
- Buying / scraping lists
- Testing in production with bogus recipients
- Misconfigured templates that 100% bounced
Sandbox protects everyone. Once you've shown a clean signal — even
just a handful of successful sends + a verified domain — production
access is fast.
## What still works in sandbox
Everything at the API level. You can:
- Verify domain identities + email-address identities
- Create configuration sets + event destinations + templates
- Send up to 200/day to whoever
- Receive webhooks normally
- Add suppression entries + check stats + check reputation
The constraints are **velocity-only** — daily quota + rate limit.
The functional surface is identical.
## Requesting production access
```bash
curl https://api.splashifypro.com/api/v1/partner/email/production-access \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"use_case": "We send transactional emails for our SaaS app — signup confirmation, password reset, payment receipts, weekly digest. ~5000 emails/day across all customers.",
"email_volume_estimate": "5000-15000 per day",
"has_unsubscribe_method": true,
"has_consent_proof": true
}'
```
Required fields:
| Field | What we want |
|---|---|
| `use_case` | A real description (≥30 chars). Says WHO you're sending to + WHY they signed up |
| `email_volume_estimate` | Realistic daily volume. Don't overstate; quotas can be raised later |
| `has_unsubscribe_method` | Confirm you have an unsubscribe link in marketing emails |
| `has_consent_proof` | Confirm recipients opted in (signup, double-opt-in, purchase, etc.) |
All four must be present. Faking them violates our AUP — we audit
periodically + downgrade accounts that lied.
## Review timeline
Most requests are reviewed within **24 business hours**. If your
request:
- Has a clear use case + verified domain + zero suppression-list
entries → typically approved within an hour
- Has a vague use case OR no verified domain yet → comes back with
questions
- Looks like list-buying / cold outreach → denied with reason
You can resubmit any number of times after a denial — fix the
flagged issue + try again.
## What approval changes
```
sandbox: true → sandbox: false
daily_send_quota: 200 → 50,000
peak_send_rate_per_second: 1 → 14
```
These are baseline production numbers. For higher volume reach
out to support. Caps can be raised per account.
## Inspecting your status
```bash
curl https://api.splashifypro.com/api/v1/partner/email/quotas \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Returns:
```json
{
"daily_send_quota": 200,
"peak_send_rate_per_second": 1,
"sandbox": true,
"sent_today": 47,
"sandbox_free_used_today": 47,
"reputation_status": "HEALTHY",
...
}
```
## Common denial reasons
- **No use case description.** Empty + 1-line submissions are
declined unread.
- **No verified domain.** We require at least one domain identity
in `VERIFIED` status before approving. Sandbox is a 5-minute
exercise — verify one + resubmit.
- **Vague description.** "Sending email to our users" isn't enough.
Tell us WHAT kind of email + HOW recipients opted in.
- **Unsubscribe gap.** Marketing email without an unsubscribe link
is illegal under CAN-SPAM, CASL, DPDP. Fix it before requesting.
- **Cold-list pattern.** "We bought a list of 100K emails" is a
decline.
================================================================================
# API Reference
Section: API Reference › API Reference
URL: https://email-docs.splashifypro.com/api-reference
================================================================================
> Every endpoint of the Splashify Pro Email API.
# API Reference
The Splashify Pro Email API is RESTful, accepts JSON-encoded
request bodies, returns JSON-encoded responses, and uses standard
HTTP response codes + verbs.
## Base URL
```
https://api.splashifypro.com/api/v1/partner/email
```
All endpoints documented in this reference are relative to this
base URL.
## Authentication
Bearer token in the `Authorization` header:
```http
Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
See [Authentication](/getting-started/authentication) for key
generation, rotation, and rate-limit details.
## Request format
POST + PATCH + PUT bodies are JSON. Set `Content-Type: application/json`.
Field names are `snake_case`. Required fields raise
`400 INVALID_REQUEST` when missing.
## Response format
Every response is JSON. Successful responses include `success: true`:
```json
{
"success": true,
"data": { ... }
}
```
Errors include a stable `error` code + a human-readable `message`:
```json
{
"success": false,
"error": "FROM_NOT_VERIFIED",
"message": "From address is not on a verified identity."
}
```
See [Errors](/api-reference/errors) for the full list.
## Resources
| Resource | Endpoints |
|---|---|
| **[Emails](/api-reference/emails/send)** | `/send`, `/send-raw`, `/send-template`, `/send-bulk`, `/emails/:id` |
| **[Templates](/api-reference/templates/list)** | CRUD + preview |
| **[Identities](/api-reference/identities/list)** | List, create, get, verify, delete |
| **[Configuration Sets](/api-reference/configuration-sets/list)** | CRUD + multi-tenant attribution |
| **[Event Destinations](/api-reference/event-destinations/list)** | Webhook destinations attached to config sets |
| **[Suppression](/api-reference/suppression/list)** | List, get, put, delete |
| **[Quotas & Stats](/api-reference/quotas/get-quota)** | Send quota, statistics, reputation, events |
| **[Production Access](/api-reference/production-access/submit)** | Sandbox → production lifecycle |
## Versioning
The API is versioned in the URL — `/api/v1/`. We will not introduce
breaking changes within a version. New optional fields, new
endpoints, and new event types may land at any time.
If we ever ship `/api/v2/`, we'll keep `/api/v1/` operational for
at least 12 months and announce the deprecation timeline at least
90 days in advance.
## Idempotency
Send endpoints are NOT idempotent — duplicate calls produce
duplicate sends. Implement application-level dedup against your
business identifier (charge_id, signup_id, etc.) before calling
`/send`.
Idempotency-key support via `Idempotency-Key` header is on the
roadmap.
================================================================================
# Pagination
Section: API Reference › API Reference
URL: https://email-docs.splashifypro.com/api-reference/pagination
================================================================================
> How list endpoints paginate.
# Pagination
List endpoints use cursor-based pagination via `limit` + `next_cursor`
query parameters.
## Request
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/identities?limit=50' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
| Parameter | Type | Default | Notes |
|---|---|---|---|
| `limit` | int | 100 | Max 1000 |
| `next_cursor` | string | — | Opaque cursor from a previous response |
## Response
```json
{
"success": true,
"identities": [...],
"count": 50,
"next_cursor": "eyJpZCI6IjEyMzQ1IiwiX3QiOjE3..."
}
```
`next_cursor` is opaque — do not parse it. Pass it back verbatim
to fetch the next page:
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/identities?limit=50&next_cursor=eyJ...' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
When `next_cursor` is absent or empty, you've reached the end.
## Endpoints that paginate
- `GET /identities`
- `GET /configuration-sets`
- `GET /templates`
- `GET /suppression`
- `GET /events`
- `GET /production-access`
## Endpoints that don't
- `GET /quotas`, `/stats`, `/reputation` — fixed-size responses
- `GET /emails/:message_id` — single-row reads
## Default ordering
Most list endpoints return newest-first. For deterministic ordering
across pages, results are stable — re-fetching with the same
cursor returns the same rows.
================================================================================
# Errors
Section: API Reference › API Reference
URL: https://email-docs.splashifypro.com/api-reference/errors
================================================================================
> API error response shape, status codes, and stable error codes.
# API Errors
Every error response from the Email API has the same shape:
```json
{
"success": false,
"error": "INSUFFICIENT_BALANCE",
"message": "Wallet balance ₹0.00. Recharge to continue sending."
}
```
| Field | Meaning |
|---|---|
| `success` | Always `false` for errors |
| `error` | Stable code — build retry/handling logic against this |
| `message` | Human-readable text. May change for clarity over time |
| `field` | (When relevant) Specific field that caused the error |
For the complete error code reference + retry guidance, see
[Knowledge Base → Errors](/knowledge-base/errors).
## HTTP status codes
| Code | Meaning |
|---|---|
| `200` | Success |
| `201` | Resource created |
| `400` | Bad request — fix your inputs |
| `401` | Missing / invalid API key |
| `402` | Insufficient wallet balance — recharge |
| `403` | Sandbox cap, sending paused, or feature locked |
| `404` | Resource not found |
| `409` | Conflict (duplicate name, etc.) |
| `429` | Rate limit hit — back off |
| `500` | Server error — retry with backoff |
| `502` | Upstream gateway error (Zoho etc.) |
| `503` | Database / dependency unavailable |
## Retry strategy
| Status | Retry? |
|---|---|
| 4xx (except 408 / 429) | No — fix the request |
| 408 / 429 | Yes — exponential backoff |
| 5xx | Yes — up to 3 attempts with backoff |
See [Knowledge Base → Errors](/knowledge-base/errors) for per-code
guidance.
================================================================================
# Connected apps (OAuth)
Section: API Reference › API Reference
URL: https://email-docs.splashifypro.com/api-reference/connected-apps
================================================================================
> Let Splashify Pro Email customers connect their account to your app with OAuth 2.0, and call the Email API for them.
# Connected apps (OAuth)
If you build an app that many Splashify Pro Email customers use, do not ask each of them for an API key. Add a **Connect Splashify Pro** button instead: the customer signs in, sees what your app asks for and clicks Allow, and your server gets a token limited to what they allowed. This is standard OAuth 2.0 with the authorization code flow and PKCE.
The full guide, with every parameter, error and limit, is on [docs.splashifypro.com](https://docs.splashifypro.com/connect-splashify-pro). This page has what is specific to Email apps.
## Get a Client ID
Create a free developer account at [dev.splashifypro.com](https://dev.splashifypro.com/signup). It is separate from your Splashify Pro Email account, and you do not need to be a customer. Click **New app**, pick **Email (email.splashifypro.com)**, add your redirect URL and permissions, and copy the client secret: we show it once.
## URLs
| | |
|---|---|
| Authorize | `https://email.splashifypro.com/oauth/authorize` |
| Token | `https://api.splashifypro.com/api/v1/oauth/token` |
| Revoke | `https://api.splashifypro.com/api/v1/oauth/revoke` |
| API base | `https://api.splashifypro.com` |
## Permissions
| Permission | What it opens |
|---|---|
| `account:read` | `GET /api/v1/oauth/me`. Always included. |
| `email.messages:send` | `POST /api/v1/partner/email/send`, `POST /api/v1/partner/email/send-template`, `POST /api/v1/partner/email/send-bulk` (verified apps only), `GET /api/v1/partner/email/identities` |
| `email.templates:read` | `GET /api/v1/partner/email/templates`, `GET /api/v1/partner/email/templates/:id`, `GET /api/v1/partner/email/templates/by-name/:name` |
| `email.reports:read` | `GET /api/v1/partner/email/emails/:message_id`, `GET /api/v1/partner/email/stats`, `GET /api/v1/partner/email/quotas`, `GET /api/v1/partner/email/events`, [`GET /api/v1/partner/email/bounce-report`](/api-reference/bounce-report) |
| `email.contacts:write` | [`GET .../audiences`](/api-reference/audiences/list), [`GET .../audiences/:id`](/api-reference/audiences/get), [`POST .../audiences/:id/contacts`](/api-reference/audiences/add-contacts), [`PATCH .../audiences/:id/contacts/:email`](/api-reference/audiences/update-contact) |
| `email.contacts:read` | [`GET .../audiences`](/api-reference/audiences/list), [`GET .../audiences/:id`](/api-reference/audiences/get), [`GET .../audiences/:id/contacts`](/api-reference/audiences/list-contacts). Verified apps only. |
Every other route, including `send-raw`, the IP allowlist, account deletion and CSV exports, is refused with `403 endpoint_not_allowed`.
## The flow
```bash
# 1. Send the customer here
https://email.splashifypro.com/oauth/authorize?response_type=code&client_id=spo_app_XXXX&redirect_uri=https%3A%2F%2Fyourapp.example%2Fsplashify%2Fcallback&scope=email.messages%3Asend%20email.reports%3Aread&state=RANDOM_STATE&code_challenge=CHALLENGE&code_challenge_method=S256
# 2. Swap the code for tokens (server side)
curl -X POST https://api.splashifypro.com/api/v1/oauth/token \
-u "spo_app_XXXX:spo_cs_YYYY" \
-d grant_type=authorization_code \
-d code=spo_ac_ZZZZ \
-d redirect_uri=https://yourapp.example/splashify/callback \
-d code_verifier=VERIFIER
# 3. Send an email for the customer
curl -X POST https://api.splashifypro.com/api/v1/partner/email/send \
-H "Authorization: Bearer spo_at_AAAA" -H "Content-Type: application/json" \
-d '{"from":"orders@customer-domain.example","to":["asha@example.com"],"subject":"Your order","html_body":"On its way.
"}'
# 4. Refresh (save the new refresh_token every time)
curl -X POST https://api.splashifypro.com/api/v1/oauth/token \
-u "spo_app_XXXX:spo_cs_YYYY" -d grant_type=refresh_token -d refresh_token=spo_rt_BBBB
```
Access tokens last 1 hour. Refresh tokens rotate on every use: always save the new one.
## Good to know
- Send the token only as `Authorization: Bearer spo_at_...`.
- Emails your app sends are charged to the customer's wallet, or to their account when it is postpaid, at their normal rates.
- Until your app is verified it can connect up to 25 accounts, send up to 500 emails a day per account, send each email to one address (`to`, `cc` and `bcc` together), and cannot use `send-bulk`.
- If the customer's account has allowed IP addresses, your calls must come from one of them, and IPv6 addresses are refused. List your server IPs on your app so customers can add them.
- Errors from the token gate use the Email API shape: `{"error": "insufficient_scope", "message": "This app was not allowed to do this."}`.
- There are no webhooks to apps yet. Poll [Get email status](/api-reference/emails/get-status) for delivery.
Read the full guide: [Connect with Splashify Pro](https://docs.splashifypro.com/connect-splashify-pro).
================================================================================
# Send email
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/send
================================================================================
> POST /api/v1/partner/email/send — send a transactional or marketing email.
# Send email
Send an email to one or more recipients. Equivalent to AWS SES
`SendEmail`.
```http
POST /api/v1/partner/email/send
```
> **Try it on the web page:** `POST /api/v1/partner/email/send` Send an email to one or more recipients.
## Request body
| Field | Type | Required | Notes |
|---|---|---|---|
| `from` | string | yes | Sender address. Must be on a verified [identity](/api-reference/identities/create). Format: `"name "` or just `addr@example.com` |
| `to` | string[] | yes | Up to 50 recipients (combined `to` + `cc` + `bcc`) |
| `cc` | string[] | no | Carbon copy |
| `bcc` | string[] | no | Blind carbon copy |
| `reply_to` | string | no | Sets the `Reply-To:` header |
| `subject` | string | yes | Subject line |
| `html_body` | string | conditional | HTML body. One of `html_body` or `text_body` is required |
| `text_body` | string | conditional | Plaintext body. Auto-derived from HTML if omitted |
| `configuration_set_name` | string | no | Routes events to this config set's destinations |
| `customer_id` | string (uuid) | no | Optional downstream customer attribution on your side |
| `category` | string | no | `transactional` (default) or `marketing` |
| `tags` | object | no | Arbitrary key-value pairs that ride on `mail.tags` in webhooks |
## Response
```json
{
"success": true,
"results": [
{
"recipient": "customer@example.com",
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued"
}
]
}
```
`results` carries one entry per recipient — `to` + `cc` + `bcc`
are flattened.
| Field | Notes |
|---|---|
| `recipient` | Lowercase + trimmed |
| `message_id` | Use this on `GET /emails/:message_id` to poll status |
| `status` | `queued` or `rejected` |
| `reason` | Present when `status: rejected` — typically `"suppressed"` |
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/send \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Welcome",
"html_body": "Welcome
",
"text_body": "Welcome."
}'
```
## Node
```js
const res = await fetch(
"https://api.splashifypro.com/api/v1/partner/email/send",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SPLASHIFY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "hello@yourcompany.com",
to: ["customer@example.com"],
subject: "Welcome",
html_body: "Welcome
",
}),
}
);
const data = await res.json();
```
## Python
```python
import requests, os
r = requests.post(
"https://api.splashifypro.com/api/v1/partner/email/send",
headers={"Authorization": f"Bearer {os.environ['SPLASHIFY_API_KEY']}"},
json={
"from": "hello@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Welcome",
"html_body": "Welcome
",
},
)
```
## Attachments
`/send` accepts an `attachments[]` array of base64-encoded files.
PDFs, images, documents, spreadsheets, ZIPs are all supported.
```json
{
"from": "billing@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Your invoice",
"html_body": "Invoice attached.
",
"attachments": [
{
"filename": "invoice.pdf",
"content_type": "application/pdf",
"content_base64": ""
}
]
}
```
**Caps:** 20 files, 10 MiB total per message. Executable / script
extensions (`.exe`, `.bat`, `.js`, `.ps1`, `.sh`, …) are blocked.
See [Send with attachments](/api-reference/emails/send-with-attachments)
for the full reference, inline-image (CID) handling, and per-language
code samples.
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 400 | `INVALID_REQUEST` | Missing required field; read `message` |
| 400 | `INVALID_ATTACHMENT` | Attachment failed validation — see [Send with attachments](/api-reference/emails/send-with-attachments) |
| 400 | `FROM_NOT_VERIFIED` | Verify your domain at `POST /identities` |
| 400 | `TOO_MANY_RECIPIENTS` | Split into multiple sends |
| 402 | `INSUFFICIENT_BALANCE` | Recharge wallet |
| 403 | `SANDBOX_QUOTA_REACHED` | Daily 200/day cap hit — request production access |
| 403 | `SENDING_PAUSED` | Account paused — contact support |
| 429 | `RATE_LIMITED` | Per-second cap exceeded — back off |
================================================================================
# Send template
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/send-template
================================================================================
> POST /api/v1/partner/email/send-template — send a saved template with variables.
# Send template
Send a previously-saved [template](/api-reference/templates/create)
with per-recipient `{{variables}}` substituted in the subject + body.
```http
POST /api/v1/partner/email/send-template
```
## Request body
```json
{
"from": "hello@yourcompany.com",
"to": ["customer@example.com"],
"template_name": "welcome",
"variables": {
"first_name": "Alex",
"company_name": "Acme"
},
"configuration_set_name": "production"
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `from` | string | yes | Verified identity |
| `to` | string[] | yes | Up to 50 recipients |
| `cc` / `bcc` | string[] | no | |
| `reply_to` | string | no | |
| `template_name` | string | yes | Friendly name from `POST /templates` |
| `variables` | object | no | `{{key}}` tokens replaced in subject + body |
| `configuration_set_name` | string | no | Event-routing scope |
| `customer_id` | string | no | Downstream customer attribution |
| `category` | string | no | `transactional` (default) or `marketing` |
## Response
Same shape as [`/send`](/api-reference/emails/send).
## Variable substitution
`{{first_name}}` and `{{ first_name }}` (with spaces) both resolve.
Missing variables stay as-is in the rendered output — they don't
raise errors. To enforce required variables, declare them on the
template via `declared_vars`.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/send-template \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourcompany.com",
"to": ["customer@example.com"],
"template_name": "welcome",
"variables": {"first_name": "Alex"}
}'
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 404 | `TEMPLATE_NOT_FOUND` | `template_name` not registered for this account |
| 400 | `RENDERING_FAILURE` | Template rendering failed (rare — most variables are forgiving) |
================================================================================
# Send bulk
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/send-bulk
================================================================================
> POST /api/v1/partner/email/send-bulk — one template, many recipients with per-recipient variables.
# Send bulk
Send a single template to up to 500 recipients across 50
destinations, each with their own variable map. Equivalent to AWS
`SendBulkTemplatedEmail`.
```http
POST /api/v1/partner/email/send-bulk
```
## Request body
```json
{
"from": "hello@yourcompany.com",
"template_name": "newsletter-june",
"default_template_data": { "campaign": "june-2026" },
"destinations": [
{
"to": ["alex@example.com"],
"replacement_data": { "first_name": "Alex" }
},
{
"to": ["brett@example.com"],
"replacement_data": { "first_name": "Brett" }
}
],
"configuration_set_name": "marketing"
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `from` | string | yes | Verified identity |
| `template_name` | string | yes | |
| `default_template_data` | object | no | Variables that apply to every recipient unless overridden |
| `destinations[]` | array | yes | Up to **50 destinations** per request |
| `destinations[].to` | string[] | yes | Up to **50 recipients per destination** |
| `destinations[].cc` | string[] | no | |
| `destinations[].bcc` | string[] | no | |
| `destinations[].replacement_data` | object | no | Per-destination variable overrides — merged onto `default_template_data` |
| `reply_to` | string | no | |
| `configuration_set_name` | string | no | |
| `customer_id` | string | no | |
| `category` | string | no | `transactional` (default) or `marketing` |
| `tags` | object | no | |
## Limits
- 50 destinations per request
- 50 recipients per destination
- 500 recipients total per request
For larger lists, paginate into multiple `/send-bulk` calls.
## Response
```json
{
"success": true,
"results": [
{
"destination": 0,
"recipient": "alex@example.com",
"message_id": "550e8400-...",
"status": "queued"
},
{
"destination": 1,
"recipient": "brett@example.com",
"message_id": "660e8400-...",
"status": "queued"
}
]
}
```
`results[].destination` is the zero-based index into your
`destinations[]` array — so you can correlate per-recipient outcomes
back to the destination you submitted.
## Variable resolution
For each destination:
1. Start with `default_template_data`
2. Merge `destinations[i].replacement_data` on top (per-destination
overrides win)
3. Substitute `{{key}}` tokens in subject + html_body + text_body
## Per-destination 4xx behaviour
If one destination fails (e.g. recipient on suppression list):
- That destination gets `status: rejected` + `reason`
- Other destinations continue processing
If a 5xx fires (template-load failure, etc.):
- The entire bulk aborts
- `failed_at` in the response indicates where we stopped
- Already-processed destinations stay queued
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/send-bulk \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourcompany.com",
"template_name": "newsletter",
"destinations": [
{"to": ["a@example.com"], "replacement_data": {"name": "A"}},
{"to": ["b@example.com"], "replacement_data": {"name": "B"}}
]
}'
```
================================================================================
# Send raw
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/send-raw
================================================================================
> POST /api/v1/partner/email/send-raw — send a pre-built MIME message.
# Send raw
Send a fully-formed MIME message (RFC 5322). For when you need
control over headers, attachments, multipart structure, or
DKIM-signed forwarding. Equivalent to AWS SES `SendRawEmail`.
```http
POST /api/v1/partner/email/send-raw
```
## Request body
```json
{
"raw_message_base64": "RnJvbTogaGVsbG9AeW91cmNvbXBhbnkuY29tDQpUbzogY3VzdG9tZXJAZXhhbXBsZS5jb20NClN1YmplY3Q6IEhlbGxvDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgNCg0KSGVsbG8h",
"configuration_set_name": "production"
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `raw_message_base64` | string | yes | base64-encoded RFC 5322 message. Max 10 MB decoded. |
| `from` | string | no | Override From-address parsing. Defaults to parsing the message's `From:` header |
| `to` | string[] | no | Override recipient parsing. Defaults to parsing `To:` header |
| `configuration_set_name` | string | no | |
| `customer_id` | string | no | |
| `category` | string | no | |
| `tags` | object | no | |
## What you control
- **All headers** — From, To, Cc, Bcc, Subject, Reply-To, Message-ID,
arbitrary `X-` headers, `In-Reply-To`, `References`
- **MIME structure** — multipart/alternative, multipart/mixed,
multipart/related (inline images via `Content-ID:`)
- **Attachments** — encode each part with the right Content-Type +
Content-Disposition + Content-Transfer-Encoding
- **Custom headers** — anything not on the reserved list
## What we override
- `Date:` — set to send time if missing
- `Message-ID:` — added if missing
- DKIM-Signature — we always sign with our shared key
- Return-Path — set to our bounce address for VERP routing
## When to use this
- Sending forwarded mail with original headers preserved
- Replying inside an existing thread (`In-Reply-To` / `References`)
- Multipart with attachments
- Custom List-Id / List-Help / List-Subscribe / List-Unsubscribe headers
For simple HTML+text sends, use [`/send`](/api-reference/emails/send) —
much less ceremony.
## Example
Building a multipart message in Node:
```js
import { createMimeMessage } from "mimetext";
const msg = createMimeMessage();
msg.setSender("hello@yourcompany.com");
msg.setRecipient("customer@example.com");
msg.setSubject("Receipt + invoice attached");
msg.addMessage({ contentType: "text/plain", data: "See attached." });
msg.addMessage({ contentType: "text/html", data: "See attached.
" });
msg.addAttachment({
filename: "invoice.pdf",
contentType: "application/pdf",
data: pdfBase64,
});
const raw = msg.asEncoded();
const rawB64 = Buffer.from(raw).toString("base64");
await fetch("https://api.splashifypro.com/api/v1/partner/email/send-raw", {
method: "POST",
headers: { Authorization: `Bearer ${API_KEY}` },
body: JSON.stringify({ raw_message_base64: rawB64 }),
});
```
================================================================================
# Send with attachments
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/send-with-attachments
================================================================================
> Attach PDFs, images, documents, and other files to your emails. Supported on /send, /send-template, and /send-bulk.
# Send with attachments
Three of the four send endpoints accept an `attachments[]` array —
`/send`, `/send-template`, and `/send-bulk`. Each attachment carries
the file bytes inline as base64; we build the `multipart/mixed`
MIME structure for you, DKIM-sign it, and ship it to the recipient
through the same pipeline as plain-body emails.
`/send-raw` always supported attachments because customers build the
MIME themselves. See [Send raw](/api-reference/emails/send-raw) for
that path.
## Attachment object
Each entry in `attachments[]` is shaped like this:
```json
{
"filename": "invoice-2026-04.pdf",
"content_type": "application/pdf",
"content_base64": "",
"content_id": "logo-header",
"inline": false
}
```
| Field | Type | Required | Meaning |
|---|---|---|---|
| `filename` | string | yes | Displayed by the recipient's mail client. Auto-sanitized — directory separators + control chars stripped. |
| `content_type` | string | optional | MIME type (e.g. `application/pdf`). When omitted, we infer from the filename extension. Falls back to `application/octet-stream`. |
| `content_base64` | string | yes | Standard base64 encoding of the file bytes. **Not** URL-safe base64 — use the standard alphabet. |
| `content_id` | string | optional | Sets `Content-ID` so HTML can reference the attachment as `
`. Pair with `inline: true`. |
| `inline` | boolean | optional | When `true`, sets `Content-Disposition: inline` (image renders in the body). When `false` (default), sets `attachment` (file appears in the recipient's attachments list). |
## Limits
| Cap | Value | What happens when exceeded |
|---|---|---|
| Files per message | **20** | Returns `400 INVALID_ATTACHMENT` with `"max 20 attachments per message"` |
| Total decoded bytes | **10 MiB** | Returns `400 INVALID_ATTACHMENT` with the bytes-cap message. Sum runs across all attachments — a single 10.1 MiB PDF is rejected just like 11 × 1 MiB files. |
| Filename length | 256 chars | Rejected at validation. |
| Content-Type length | 256 chars | Rejected at validation. |
## Blocked file types
Executable + script extensions are refused at the API layer because
recipient mail providers (Gmail, Outlook, corporate gateways) block
them outright — sending one would cost a wallet deduction for a
guaranteed bounce. Blocked extensions:
`.exe` `.bat` `.cmd` `.com` `.scr` `.msi` `.vbs` `.vbe` `.js` `.jse`
`.wsf` `.wsh` `.ps1` `.dll` `.jar` `.app` `.sh` `.pl` `.py` `.lnk`
Workaround: bundle them inside a `.zip`. ZIPs are allowed.
## Example — single PDF
```bash
ATT=$(base64 -i invoice.pdf | tr -d '\n')
curl https://api.splashifypro.com/api/v1/partner/email/send \
-H "Authorization: Bearer pk_live_…" \
-H "Content-Type: application/json" \
-d "{
\"from\": \"billing@yourcompany.com\",
\"to\": [\"customer@example.com\"],
\"subject\": \"Invoice for March\",
\"html_body\": \"Hi! Your invoice for March is attached.
\",
\"attachments\": [
{
\"filename\": \"invoice-march-2026.pdf\",
\"content_type\": \"application/pdf\",
\"content_base64\": \"$ATT\"
}
]
}"
```
## Example — Node.js (multiple files)
```js
import { readFileSync } from 'node:fs'
const toAtt = (path, contentType) => ({
filename: path.split('/').pop(),
content_type: contentType,
content_base64: readFileSync(path).toString('base64'),
})
const r = await fetch('https://api.splashifypro.com/api/v1/partner/email/send', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.SPLASHIFY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: 'billing@yourcompany.com',
to: ['customer@example.com'],
subject: 'Your March documents',
html_body: 'Attached: invoice + receipt + signed contract.
',
attachments: [
toAtt('./invoice.pdf', 'application/pdf'),
toAtt('./receipt.pdf', 'application/pdf'),
toAtt('./contract.pdf', 'application/pdf'),
],
}),
})
console.log(await r.json())
```
## Example — Python
```python
import base64, requests, os
def att(path, ct):
with open(path, 'rb') as f:
return {
'filename': os.path.basename(path),
'content_type': ct,
'content_base64': base64.b64encode(f.read()).decode(),
}
r = requests.post(
'https://api.splashifypro.com/api/v1/partner/email/send',
headers={'Authorization': f"Bearer {os.environ['SPLASHIFY_API_KEY']}"},
json={
'from': 'support@yourcompany.com',
'to': ['customer@example.com'],
'subject': 'Attached photo',
'html_body': 'Here\'s the photo from your visit.
',
'attachments': [att('./photo.jpg', 'image/jpeg')],
},
)
print(r.json())
```
## Example — Inline image (CID reference)
When you want the image to render **inside** the email body instead
of as a separate downloadable file, use `inline: true` + `content_id`,
then reference it from your HTML with `cid:`:
```json
{
"from": "newsletter@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Today's newsletter",
"html_body": "Welcome!
",
"attachments": [
{
"filename": "banner.png",
"content_type": "image/png",
"content_base64": "iVBORw0KGgo…",
"content_id": "hero-banner",
"inline": true
}
]
}
```
Most modern mail clients (Gmail web + mobile, Outlook, Apple Mail)
render inline images correctly. A few hardened corporate gateways
strip them — the file falls back to a regular attachment in those.
## Errors
| Status | Code | Reason |
|---|---|---|
| 400 | `INVALID_ATTACHMENT` | Too many files, total size exceeded, blocked extension, invalid base64, or empty content |
| 400 | `FROM_NOT_VERIFIED` | From-address not on a verified identity (same as plain send) |
| 402 | `INSUFFICIENT_BALANCE` | Wallet too low to cover the per-recipient send cost |
## Pricing
Attachments do **not** incur a per-byte charge. You're billed at the
per-recipient rate (₹0.03 per email, marketing or transactional)
regardless of attachment size. The only practical cost is the 10 MiB
total cap which limits how big each individual message can be.
## Best practices
- **Compress where you can.** A PDF with images compressed to 200 dpi
vs 600 dpi can be 5× smaller and still look perfect at on-screen
resolution. Smaller messages deliver faster + use less of your
10 MiB budget.
- **Use links for files > 5 MiB.** Recipient mail providers throttle
large messages and some flag them as suspicious. For anything
bigger, host on object storage and put a download link in the body.
- **Set `content_type` explicitly when you can** — saves us a
filename-extension guess. The mail client's "open with" picker
uses this header.
- **Prefer real filenames.** Generic `attachment.pdf` is allowed but
hurts deliverability slightly (Gmail's spam filter weights
template-shaped filenames). `invoice-march-2026.pdf` is better.
## Send-bulk semantics
When you call `/send-bulk` with `attachments[]`, the same files are
attached to **every** destination's email — there's no per-recipient
attachment override. If you need different attachments per
recipient, call `/send-template` once per recipient instead.
================================================================================
# Get email status
Section: API Reference › Emails
URL: https://email-docs.splashifypro.com/api-reference/emails/get-status
================================================================================
> GET /api/v1/partner/email/emails/:message_id — check the delivery state of a sent email.
# Get email status
Check whether a sent email has been queued, delivered, bounced, or
rejected.
```http
GET /api/v1/partner/email/emails/:message_id
```
## Path parameters
| Field | Notes |
|---|---|
| `message_id` | UUID returned from `/send` / `/send-template` / `/send-bulk` / `/send-raw` |
## Response
```json
{
"success": true,
"message_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "delivered",
"from_address": "hello@yourcompany.com",
"to_address": "customer@example.com",
"subject": "Welcome",
"category": "transactional",
"smtp_message_id": "",
"created_at": "2026-05-03T12:34:56Z",
"sent_at": "2026-05-03T12:34:57Z",
"delivered_at": "2026-05-03T12:34:57Z"
}
```
## Status values
| Status | Meaning |
|---|---|
| `queued` | API accepted; not yet delivered to recipient MX |
| `sent` | Sent to recipient MX (collapsed with `delivered` for our pipeline) |
| `delivered` | Recipient MX returned 250 OK |
| `bounced` | Hard or soft-bounce-exhausted bounce |
| `complained` | Recipient marked as spam (FBL report received) |
| `rejected` | Refused pre-SMTP (suppression list, sandbox cap, etc.) |
| `failed` | Soft bounce retries exhausted |
## Bounce details
When `status: bounced`:
```json
{
"status": "bounced",
"bounced_at": "2026-05-03T12:35:01Z",
"bounce_type": "Permanent",
"bounce_reason": "no_such_user"
}
```
## When to use polling vs webhooks
**Polling is the wrong default.** Webhooks (set up via
[event destinations](/api-reference/event-destinations/create)) push
status changes to your server within seconds without you polling.
Use this endpoint for:
- One-off lookups (debugging a specific send)
- UI surfaces showing live status (after a webhook update lands)
- Reconciliation jobs (check that webhook delivery wasn't dropped)
Don't poll every send every 5 seconds — wasteful for both sides.
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 404 | `MESSAGE_NOT_FOUND` | Message id doesn't match any send. Possible: wrong account, malformed id, or message older than retention window |
## Retention
Outbox rows are retained for 30 days. After that, older rows
return 404. For long-term audit, listen to the
`Send` / `Delivery` / `Bounce` webhook events and persist them
yourself.
================================================================================
# List templates
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/list
================================================================================
> GET /api/v1/partner/email/templates
# List templates
```http
GET /api/v1/partner/email/templates
```
## Response
```json
{
"success": true,
"templates": [
{
"template_id": "tpl_550e8400-...",
"template_name": "welcome",
"subject": "Welcome to {{company_name}}",
"declared_vars": ["first_name", "company_name"],
"created_at": "2026-05-03T12:00:00Z",
"updated_at": "2026-05-03T12:00:00Z"
}
],
"count": 1
}
```
`html` + `text` + `react_email_json` are NOT included in list
responses to keep the payload small. Use [GET /templates/:id](/api-reference/templates/get)
or [GET /templates/by-name/:name](/api-reference/templates/get-by-name)
to fetch full content.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/templates \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Create template
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/create
================================================================================
> POST /api/v1/partner/email/templates
# Create template
Save a reusable template. Reference it on `/send-template` or
`/send-bulk` by its `template_name`.
```http
POST /api/v1/partner/email/templates
```
## Request body
```json
{
"template_name": "welcome",
"subject": "Welcome to {{company_name}}, {{first_name}}",
"html": "Hi {{first_name}} 👋
Thanks for joining {{company_name}}.
",
"text": "Hi {{first_name}}. Thanks for joining {{company_name}}.",
"declared_vars": ["first_name", "company_name"]
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `template_name` | string | yes | Unique per account. Lowercase letters / numbers / `_` `-`, max 64 chars |
| `subject` | string | yes | Variables can be used here too |
| `html` | string | conditional | One of `html` or `react_email_json` is required |
| `text` | string | no | Plaintext fallback. Auto-derived from HTML if omitted |
| `react_email_json` | string | conditional | Visual-editor JSON. Renders to `html` + `text` server-side |
| `declared_vars` | string[] | no | Variable names the template uses. Surfaced on the panel + helps catch typos |
## Variable syntax
Both `{{var_name}}` and `{{ var_name }}` (with spaces) work.
Missing variables at send time stay as the literal `{{name}}` in
the rendered output rather than raising an error — this keeps
hot-path sends forgiving. To enforce required variables, declare
them in `declared_vars` and validate yourself before calling
`/send-template`.
## Response
```json
{
"success": true,
"template": {
"template_id": "tpl_550e8400-...",
"template_name": "welcome",
"subject": "Welcome to {{company_name}}, {{first_name}}",
"html": "Hi {{first_name}}...",
"text": "Hi {{first_name}}...",
"declared_vars": ["first_name", "company_name"],
"created_at": "...",
"updated_at": "..."
}
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/templates \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template_name": "welcome",
"subject": "Hi {{first_name}}",
"html": "
Hi {{first_name}}
"
}'
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 409 | `TEMPLATE_NAME_TAKEN` | Another template with that name exists |
| 400 | `INVALID_REQUEST` | Bad name format or missing both `html` and `react_email_json` |
| 400 | `TEMPLATE_LIMIT_REACHED` | 500-template cap per account |
================================================================================
# Get template
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/get
================================================================================
> GET /api/v1/partner/email/templates/:id
# Get template
```http
GET /api/v1/partner/email/templates/:id
```
Fetch full template content by template_id.
## Response
```json
{
"success": true,
"template": {
"template_id": "tpl_550e8400-...",
"template_name": "welcome",
"subject": "Welcome",
"html": "Hi {{first_name}}
",
"text": "Hi {{first_name}}",
"react_email_json": "...",
"declared_vars": ["first_name"],
"created_at": "...",
"updated_at": "..."
}
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/templates/tpl_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Get template by name
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/get-by-name
================================================================================
> GET /api/v1/partner/email/templates/by-name/:name
# Get template by name
```http
GET /api/v1/partner/email/templates/by-name/:name
```
Fetch a template by its `template_name`. Same response shape as
[GET /templates/:id](/api-reference/templates/get).
Useful when your code only knows the friendly name (e.g. from a
config file) and not the UUID.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/templates/by-name/welcome \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Update template
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/update
================================================================================
> PATCH /api/v1/partner/email/templates/:id
# Update template
```http
PATCH /api/v1/partner/email/templates/:id
```
All fields optional — pass only what's changing. `template_name`
cannot be changed; create a new template under the new name and
delete the old one.
## Request body
```json
{
"subject": "Welcome to {{company_name}}",
"html": "Updated welcome 👋
",
"text": "Updated welcome.",
"declared_vars": ["company_name"]
}
```
| Field | Type | Notes |
|---|---|---|
| `subject` | string | |
| `html` | string | |
| `text` | string | |
| `react_email_json` | string | When supplied, re-renders to fresh `html` + `text` |
| `declared_vars` | string[] | Replaces existing list |
## cURL
```bash
curl -X PATCH \
https://api.splashifypro.com/api/v1/partner/email/templates/tpl_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject": "New subject"}'
```
================================================================================
# Delete template
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/delete
================================================================================
> DELETE /api/v1/partner/email/templates/:id
# Delete template
```http
DELETE /api/v1/partner/email/templates/:id
```
Removes the template + its name alias. In-flight `/send-template`
calls referencing this template by name return
`404 TEMPLATE_NOT_FOUND` after the delete commits.
## cURL
```bash
curl -X DELETE \
https://api.splashifypro.com/api/v1/partner/email/templates/tpl_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## What this does NOT do
- Does NOT recall already-queued sends that referenced this template.
- Does NOT remove historical send records — `GET /emails/:message_id`
still returns the original `template_id` for past sends.
================================================================================
# Preview template
Section: API Reference › Templates
URL: https://email-docs.splashifypro.com/api-reference/templates/preview
================================================================================
> POST /api/v1/partner/email/templates/preview — render visual-editor JSON without persisting.
# Preview template
Render a `react_email_json` payload to HTML + plaintext without
saving a template. Useful for live preview UIs in your panel
before you persist via `POST /templates`.
```http
POST /api/v1/partner/email/templates/preview
```
## Request body
```json
{
"react_email_json": "{...}",
"variables": {
"first_name": "Alex",
"company_name": "Acme"
}
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `react_email_json` | string | yes | Visual-editor block JSON |
| `variables` | object | no | Substitution map applied to the rendered output |
## Response
```json
{
"success": true,
"html": "...",
"text": "..."
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/templates/preview \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"react_email_json": "...",
"variables": {"first_name": "Alex"}
}'
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 503 | `RENDERER_UNAVAILABLE` | Template rendering service is temporarily down — retry |
| 502 | `RENDER_FAILED` | The JSON couldn't be compiled. Read the message |
================================================================================
# List audiences
Section: API Reference › Audiences
URL: https://email-docs.splashifypro.com/api-reference/audiences/list
================================================================================
> GET /api/v1/partner/email/audiences: every audience of your account with its contact counts.
# List audiences
Every audience (contact list) of your account, with how many contacts are subscribed, unsubscribed and bounced.
```http
GET /api/v1/partner/email/audiences
```
Connected apps need `email.contacts:write` or `email.contacts:read`. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Response
```json
{
"success": true,
"audiences": [
{
"audience_id": "8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b",
"name": "Newsletter",
"description": "Monthly product news",
"subscribed": 1240,
"unsubscribed": 37,
"bounced": 12,
"total": 1289,
"created_at": "2026-08-14T06:30:00Z",
"updated_at": "2026-09-30T11:02:00Z"
}
]
}
```
| Field | Meaning |
|---|---|
| `audience_id` | Use it in the other audience calls |
| `subscribed`, `unsubscribed`, `bounced` | Contacts in each status |
| `total` | Every contact ever added to the audience |
All audiences come in one answer; there is no paging.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/audiences \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 401 | `unauthorized` | Missing or wrong key or token |
| 500 | `database error` | Try again in a moment |
================================================================================
# Get audience
Section: API Reference › Audiences
URL: https://email-docs.splashifypro.com/api-reference/audiences/get
================================================================================
> GET /api/v1/partner/email/audiences/:id: one audience with its contact counts.
# Get audience
One audience with its contact counts.
```http
GET /api/v1/partner/email/audiences/:id
```
Connected apps need `email.contacts:write` or `email.contacts:read`. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Path parameters
| Field | Type | Notes |
|---|---|---|
| `id` | uuid | The `audience_id` from [List audiences](/api-reference/audiences/list) |
## Response
```json
{
"success": true,
"audience": {
"audience_id": "8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b",
"name": "Newsletter",
"description": "Monthly product news",
"subscribed": 1240,
"unsubscribed": 37,
"bounced": 12,
"total": 1289,
"created_at": "2026-08-14T06:30:00Z",
"updated_at": "2026-09-30T11:02:00Z"
}
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/audiences/8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 400 | `invalid audience_id` | `id` is not a UUID |
| 401 | `unauthorized` | Missing or wrong key or token |
| 404 | `audience not found` | No audience with this id on your account |
================================================================================
# List contacts
Section: API Reference › Audiences
URL: https://email-docs.splashifypro.com/api-reference/audiences/list-contacts
================================================================================
> GET /api/v1/partner/email/audiences/:id/contacts: the contacts of an audience, 50 at a time, in email order.
# List contacts
The contacts of one audience, 50 at a time, in email address order.
```http
GET /api/v1/partner/email/audiences/:id/contacts
```
Connected apps need `email.contacts:read`, which only verified apps can have. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Parameters
| Field | In | Notes |
|---|---|---|
| `id` | path | The `audience_id` |
| `cursor` | query | The `next_cursor` of the previous page. Leave it out for the first page. |
| `status` | query | Only contacts in this status: `subscribed`, `unsubscribed`, `bounced` or `complained` |
## Response
```json
{
"success": true,
"contacts": [
{
"email": "asha@example.com",
"status": "subscribed",
"first_name": "Asha",
"last_name": "Rao",
"attributes": { "city": "Pune" },
"created_at": "2026-09-01T08:00:00Z"
}
],
"next_cursor": "asha@example.com"
}
```
`next_cursor` is there when more contacts may follow. Pass it as `cursor` to get the next page, and stop when it is missing.
With a `status` filter, each page is read first and then filtered, so a page can hold fewer than 50 contacts, or none, while `next_cursor` is still there. Keep paging until `next_cursor` is missing.
## cURL
```bash
curl "https://api.splashifypro.com/api/v1/partner/email/audiences/8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b/contacts?status=subscribed" \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 400 | `invalid audience_id` | `id` is not a UUID |
| 401 | `unauthorized` | Missing or wrong key or token |
================================================================================
# Add contacts
Section: API Reference › Audiences
URL: https://email-docs.splashifypro.com/api-reference/audiences/add-contacts
================================================================================
> POST /api/v1/partner/email/audiences/:id/contacts: add contacts to an audience.
# Add contacts
Add one or more contacts to an audience.
```http
POST /api/v1/partner/email/audiences/:id/contacts
```
Connected apps need `email.contacts:write`. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Request body
```json
{
"contacts": [
{ "email": "asha@example.com", "first_name": "Asha", "last_name": "Rao", "attributes": { "city": "Pune" } },
{ "email": "vikram@example.com" }
]
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `contacts` | object[] | yes | The contacts to add |
| `contacts[].email` | string | yes | Stored in lower case. An address without `@` is skipped. |
| `contacts[].first_name` | string | no | |
| `contacts[].last_name` | string | no | |
| `contacts[].attributes` | object | no | Text values, for template variables |
Use an `audience_id` from [List audiences](/api-reference/audiences/list).
## Response
```json
{ "success": true, "added": 1, "skipped": 1 }
```
- A new email is added as `subscribed` and counts in `added`.
- An email already in the audience counts in `skipped`. Its first and last name are updated, and its status stays as it was, so an unsubscribed contact is never subscribed again this way.
- An invalid address counts in `skipped`.
## cURL
```bash
curl -X POST https://api.splashifypro.com/api/v1/partner/email/audiences/8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b/contacts \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contacts":[{"email":"asha@example.com","first_name":"Asha"}]}'
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 400 | `invalid audience_id` | `id` is not a UUID |
| 400 | `invalid body` | The body is not JSON in the shape above |
| 401 | `unauthorized` | Missing or wrong key or token |
================================================================================
# Update contact
Section: API Reference › Audiences
URL: https://email-docs.splashifypro.com/api-reference/audiences/update-contact
================================================================================
> PATCH /api/v1/partner/email/audiences/:id/contacts/:email: change a contact's status or name.
# Update contact
Change the status or name of a contact in an audience, for example to unsubscribe someone who asked you to stop.
```http
PATCH /api/v1/partner/email/audiences/:id/contacts/:email
```
Connected apps need `email.contacts:write`. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Path parameters
| Field | Notes |
|---|---|
| `id` | The `audience_id` |
| `email` | The contact's email address, **URL-encoded**: `asha@example.com` becomes `asha%40example.com`, and `a+b@example.com` becomes `a%2Bb%40example.com`. Any case; it is matched in lower case. |
## Request body
Send any of these fields; the others stay as they are.
```json
{ "status": "unsubscribed", "first_name": "Asha", "last_name": "Rao" }
```
| Field | Notes |
|---|---|
| `status` | `subscribed`, `unsubscribed`, `bounced` or `complained`. Any other value is saved as `subscribed`. |
| `first_name` | |
| `last_name` | |
Update only contacts that are in the audience: add a new address with [Add contacts](/api-reference/audiences/add-contacts) first.
## Response
```json
{ "success": true }
```
## cURL
```bash
curl -X PATCH https://api.splashifypro.com/api/v1/partner/email/audiences/8b2f6c1e-9a4d-4f1b-b6a2-1c3d5e7f9a0b/contacts/asha%40example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"unsubscribed"}'
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 400 | `invalid audience_id` | `id` is not a UUID |
| 400 | `email required` | The email in the path is empty |
| 400 | `invalid body` | The body is not JSON in the shape above |
| 401 | `unauthorized` | Missing or wrong key or token |
================================================================================
# List identities
Section: API Reference › Identities
URL: https://email-docs.splashifypro.com/api-reference/identities/list
================================================================================
> GET /api/v1/partner/email/identities
# List identities
```http
GET /api/v1/partner/email/identities
```
Lists every sending identity (verified domains + email addresses)
configured for your account.
## Response
```json
{
"success": true,
"identities": [
{
"identity_type": "DOMAIN",
"identity_value": "yourcompany.com",
"status": "VERIFIED",
"spf_ok": true,
"dkim_ok": true,
"dmarc_ok": true,
"verified_at": "2026-05-03T12:00:00Z",
"created_at": "2026-05-03T11:55:00Z"
}
],
"count": 1
}
```
## Status values
| Status | Meaning |
|---|---|
| `PENDING` | DNS records not yet verified |
| `VERIFIED` | All three records pass — ready to send |
| `FAILED` | Verification failed multiple times — re-trigger via `POST /verify` |
| `TEMPORARY_FAILURE` | DNS lookup error — retry |
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Create identity
Section: API Reference › Identities
URL: https://email-docs.splashifypro.com/api-reference/identities/create
================================================================================
> POST /api/v1/partner/email/identities — register a new domain or email address.
# Create identity
Register a new sending identity. Returns the DNS records you need
to publish (for `DOMAIN`) or a verification token (for
`EMAIL_ADDRESS`).
```http
POST /api/v1/partner/email/identities
```
## Request body
```json
{
"identity_type": "DOMAIN",
"identity_value": "yourcompany.com"
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `identity_type` | string | yes | `DOMAIN` or `EMAIL_ADDRESS` |
| `identity_value` | string | yes | The domain (e.g. `yourcompany.com`) or full email address |
## Response
For `DOMAIN`:
```json
{
"success": true,
"identity_type": "DOMAIN",
"identity_value": "yourcompany.com",
"status": "PENDING",
"dns_records": {
"spf": {"type": "TXT", "hostname": "yourcompany.com", "value": "v=spf1 include:_spf.mail.splashifypro.com ~all"},
"dkim": {"type": "CNAME", "hostname": "splashify._domainkey.yourcompany.com", "value": "splashify._domainkey.mail.splashifypro.com"},
"dmarc": {"type": "TXT", "hostname": "_dmarc.yourcompany.com", "value": "v=DMARC1; p=quarantine; rua=mailto:dmarc@splashifypro.com"}
}
}
```
Publish every record in `dns_records` on your DNS provider (SPF, DKIM and DMARC, plus `dkim2` and `return_path` when present), then call
[`/verify`](/api-reference/identities/verify) to confirm.
For `EMAIL_ADDRESS`:
```json
{
"success": true,
"identity_type": "EMAIL_ADDRESS",
"identity_value": "alerts@yourcompany.com",
"status": "PENDING",
"verification_token": "abc123..."
}
```
We email a confirmation link to the address. Click it (or POST
the token via `/verify`) to mark the identity verified.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"identity_type": "DOMAIN", "identity_value": "yourcompany.com"}'
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 400 | `INVALID_REQUEST` | Bad domain format / invalid email |
| 400 | `PUBLIC_DOMAIN_BLOCKED` | Public-mail-provider domain (gmail.com, yahoo.com, etc.) — not allowed |
| 400 | `IDENTITY_LIMIT_REACHED` | Sandbox account hit the 25-identity cap. The cap is **lifted automatically** once production access is approved — see below. |
### About the identity cap
| Account state | Identity cap |
|---|---|
| **Sandbox** (default for new accounts) | **25** identities (domains + email addresses combined) |
| **Production access approved** | **Unlimited** |
Most accounts only need 1 – 5 identities even at full scale.
Verifying a single domain (e.g. `mail.yourcompany.com`) authorizes
every `@mail.yourcompany.com` to send, so you rarely need
to register individual email addresses separately.
#### Lifting the cap
The cleanest path is **production access** — admin reviews your use
case once, approves, and the identity cap is removed automatically
along with the daily-send cap (200/day → 50,000/day) and peak-rate
cap (1/sec → 14/sec). Submit at `POST /partner/email/production-access`
or via the **Production Access** tab in Splashify Pro Email.
#### Working around the cap before production access
If you're still in sandbox and genuinely need to register a new
identity but already have 25:
1. List what's registered — `GET /partner/email/identities`.
2. Delete one you no longer send from —
`DELETE /partner/email/identities/{type}/{value}`.
3. The slot frees immediately; re-try the create call.
If your sandbox use case legitimately needs more than 25 identities
(rare — usually means you're adding one `EMAIL_ADDRESS` per customer
where verifying the parent domain would do), email
support@splashifypro.in with the use case.
================================================================================
# Get identity
Section: API Reference › Identities
URL: https://email-docs.splashifypro.com/api-reference/identities/get
================================================================================
> GET /api/v1/partner/email/identities/:type/:value
# Get identity
```http
GET /api/v1/partner/email/identities/:type/:value
```
Returns the current state of one identity, including a **live
DNS-check overlay** so you see real-time `spf_ok` / `dkim_ok` /
`dmarc_ok` flags reflecting what's actually published right now.
## Path parameters
| Field | Notes |
|---|---|
| `type` | `DOMAIN` or `EMAIL_ADDRESS` |
| `value` | The domain or email address |
## Response
```json
{
"success": true,
"identity": {
"identity_type": "DOMAIN",
"identity_value": "yourcompany.com",
"status": "VERIFIED",
"spf_ok": true,
"dkim_ok": true,
"dmarc_ok": true,
"dkim_selector": "splashify",
"verified_at": "2026-05-03T12:00:00Z",
"created_at": "2026-05-03T11:55:00Z",
"dns_records": {
"spf": {"pass": true, "found": "v=spf1 include:_spf.mail.splashifypro.com ~all", "expected": "..."},
"dkim": {"pass": true, "found": "CNAME splashify._domainkey.mail.splashifypro.com", "expected": "..."},
"dmarc": {"pass": true, "found": "v=DMARC1; p=quarantine; ...", "expected": "..."}
}
}
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Verify identity
Section: API Reference › Identities
URL: https://email-docs.splashifypro.com/api-reference/identities/verify
================================================================================
> POST /api/v1/partner/email/identities/:type/:value/verify — re-run DNS / token check.
# Verify identity
Re-run DNS verification for a domain identity (after publishing
records), or confirm an email-address identity (with the
verification token).
```http
POST /api/v1/partner/email/identities/:type/:value/verify
```
## Domain verification
```bash
curl -X POST \
https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com/verify \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Response:
```json
{
"success": true,
"status": "VERIFIED",
"spf_ok": true,
"dkim_ok": true,
"dmarc_ok": true,
"dns_records": { ... }
}
```
When all three records pass, status flips to `VERIFIED`. If any
record fails, status stays `PENDING` (or flips to `FAILED` after
multiple unsuccessful attempts) — the per-record `pass` flags tell
you which records to fix.
## Email-address verification
```bash
curl -X POST \
https://api.splashifypro.com/api/v1/partner/email/identities/EMAIL_ADDRESS/alerts@yourcompany.com/verify \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"token": "abc123..."}'
```
The token is the `verification_token` returned from
[`POST /identities`](/api-reference/identities/create).
## Rate limiting
We rate-limit re-verify calls to **once per 60 seconds per identity**
to prevent DNS-server hammering. Calls inside the window return
`429 RATE_LIMITED`.
## Auto-recheck behaviour
We automatically re-check:
- **Pending** identities every 1 hour for 7 days
- **Verified** identities every 24 hours
If a verified domain's records disappear, status flips back to
`PENDING` and we email you. You can always trigger a manual
re-check via this endpoint.
================================================================================
# Delete identity
Section: API Reference › Identities
URL: https://email-docs.splashifypro.com/api-reference/identities/delete
================================================================================
> DELETE /api/v1/partner/email/identities/:type/:value
# Delete identity
Remove a sending identity. In-flight sends from that identity
complete normally; new sends are rejected with `FROM_NOT_VERIFIED`.
```http
DELETE /api/v1/partner/email/identities/:type/:value
```
## cURL
```bash
curl -X DELETE \
https://api.splashifypro.com/api/v1/partner/email/identities/DOMAIN/yourcompany.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Response
```json
{ "success": true }
```
## What this does NOT do
- Does NOT delete your DNS records — you can leave them published
and re-add the identity later
- Does NOT clear the suppression list — recipient suppressions are
account-wide, not per-identity
- Does NOT remove webhook destinations — those live on configuration
sets, not identities
## Reactivating
To re-add a deleted identity, just `POST /identities` again with
the same value. If your DNS records are still published, the
verification will pass on first check and the identity is back to
`VERIFIED` immediately.
================================================================================
# List configuration sets
Section: API Reference › Configuration Sets
URL: https://email-docs.splashifypro.com/api-reference/configuration-sets/list
================================================================================
> GET /api/v1/partner/email/configuration-sets
# List configuration sets
```http
GET /api/v1/partner/email/configuration-sets
```
## Query parameters
| Field | Type | Notes |
|---|---|---|
| `customer_id` | uuid | Filter by downstream customer attribution |
## Response
```json
{
"success": true,
"configuration_sets": [
{
"config_set_id": "cs_550e8400-...",
"name": "production",
"description": "Production transactional sends",
"sending_enabled": true,
"reputation_tracking_enabled": true,
"suppression_options": "BOUNCE_AND_COMPLAINT",
"tags": {"env": "prod"},
"created_at": "2026-05-03T12:00:00Z",
"updated_at": "2026-05-03T12:00:00Z"
}
],
"count": 1
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Create configuration set
Section: API Reference › Configuration Sets
URL: https://email-docs.splashifypro.com/api-reference/configuration-sets/create
================================================================================
> POST /api/v1/partner/email/configuration-sets
# Create configuration set
```http
POST /api/v1/partner/email/configuration-sets
```
## Request body
```json
{
"name": "production",
"description": "Production transactional sends",
"customer_id": "cust_550e8400-...",
"sending_enabled": true,
"reputation_tracking_enabled": true,
"suppression_options": "BOUNCE_AND_COMPLAINT",
"tags": { "env": "prod", "team": "platform" }
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `name` | string | yes | Unique per account, alphanumeric + `_` `-`, 1-256 chars |
| `description` | string | no | |
| `customer_id` | uuid | no | Downstream end-customer attribution |
| `sending_enabled` | bool | no | Default `true` |
| `reputation_tracking_enabled` | bool | no | Default `true` |
| `suppression_options` | string | no | `NONE` / `BOUNCE` / `COMPLAINT` / `BOUNCE_AND_COMPLAINT` (default) |
| `custom_redirect_domain` | string | no | Per-config-set click-tracking host |
| `tags` | object | no | Arbitrary key-value pairs surfaced on `mail.tags` in webhooks |
## Response
```json
{
"success": true,
"configuration_set": {
"config_set_id": "cs_550e8400-...",
"name": "production",
...
}
}
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 409 | `CONFIG_SET_NAME_TAKEN` | Another set with that name exists |
| 400 | `INVALID_REQUEST` | Bad `suppression_options` value or missing `name` |
================================================================================
# Get configuration set
Section: API Reference › Configuration Sets
URL: https://email-docs.splashifypro.com/api-reference/configuration-sets/get
================================================================================
> GET /api/v1/partner/email/configuration-sets/:id
# Get configuration set
```http
GET /api/v1/partner/email/configuration-sets/:id
```
## Path parameters
| Field | Notes |
|---|---|
| `id` | The `config_set_id` returned from `POST /configuration-sets` |
## Response
```json
{
"success": true,
"configuration_set": {
"config_set_id": "cs_550e8400-...",
"name": "production",
"description": "...",
"sending_enabled": true,
"reputation_tracking_enabled": true,
"suppression_options": "BOUNCE_AND_COMPLAINT",
"tags": {"env": "prod"},
"created_at": "...",
"updated_at": "..."
}
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Update configuration set
Section: API Reference › Configuration Sets
URL: https://email-docs.splashifypro.com/api-reference/configuration-sets/update
================================================================================
> PATCH /api/v1/partner/email/configuration-sets/:id
# Update configuration set
```http
PATCH /api/v1/partner/email/configuration-sets/:id
```
All fields are optional — pass only what you want to change.
Common use: kill-switch a set with `sending_enabled: false`,
toggle suppression behaviour, rename.
## Request body
```json
{
"name": "production-v2",
"sending_enabled": false,
"suppression_options": "BOUNCE",
"tags": { "env": "prod", "version": "v2" }
}
```
| Field | Type | Notes |
|---|---|---|
| `name` | string | Must remain unique per account |
| `description` | string | |
| `sending_enabled` | bool | Disable to halt new sends through this set |
| `reputation_tracking_enabled` | bool | |
| `suppression_options` | string | `NONE` / `BOUNCE` / `COMPLAINT` / `BOUNCE_AND_COMPLAINT` |
| `custom_redirect_domain` | string | |
| `tags` | object | Replaces existing tags entirely (not merge) |
## cURL
```bash
curl -X PATCH \
https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sending_enabled": false}'
```
================================================================================
# Delete configuration set
Section: API Reference › Configuration Sets
URL: https://email-docs.splashifypro.com/api-reference/configuration-sets/delete
================================================================================
> DELETE /api/v1/partner/email/configuration-sets/:id
# Delete configuration set
Cascades — deletes the configuration set + every event destination
attached to it.
```http
DELETE /api/v1/partner/email/configuration-sets/:id
```
## cURL
```bash
curl -X DELETE \
https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_550e8400-... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## What this does NOT do
- Doesn't delete the historical events / stats associated with this
config set — they're still accessible via `GET /events` and
`GET /stats?config_set_id=...`
- Doesn't suppress in-flight sends. New sends referencing the
deleted set's name return `404 CONFIG_SET_NOT_FOUND`.
## Reactivating
Create a new config set with the same `name`. It gets a fresh
`config_set_id` — old `message_id` records still reference the old
id but new sends use the new one.
================================================================================
# List event destinations
Section: API Reference › Event Destinations
URL: https://email-docs.splashifypro.com/api-reference/event-destinations/list
================================================================================
> GET /api/v1/partner/email/configuration-sets/:id/event-destinations
# List event destinations
```http
GET /api/v1/partner/email/configuration-sets/:id/event-destinations
```
Returns every webhook destination attached to a configuration set.
## Response
```json
{
"success": true,
"event_destinations": [
{
"destination_id": "ed_550e8400-...",
"name": "production-webhook",
"destination_type": "WEBHOOK",
"webhook_url": "https://yourapp.com/webhooks/email",
"is_signed": true,
"matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"],
"enabled": true,
"created_at": "2026-05-03T12:00:00Z",
"updated_at": "2026-05-03T12:00:00Z"
}
],
"count": 1
}
```
`is_signed` is `true` when a `webhook_secret` is configured. The
secret value itself is **never** returned by the API — to verify
you have the right one, send a test event and check that your
endpoint's HMAC validation passes.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_.../event-destinations \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Create event destination
Section: API Reference › Event Destinations
URL: https://email-docs.splashifypro.com/api-reference/event-destinations/create
================================================================================
> POST /api/v1/partner/email/configuration-sets/:id/event-destinations — add a webhook URL.
# Create event destination
Add a webhook destination to a configuration set. Events from sends
referencing the config set will be POSTed to your URL with an
HMAC-signed body.
```http
POST /api/v1/partner/email/configuration-sets/:id/event-destinations
```
## Request body
```json
{
"name": "production-webhook",
"destination_type": "WEBHOOK",
"webhook_url": "https://yourapp.com/webhooks/email",
"webhook_secret": "your-shared-secret-min-16-chars",
"matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"],
"enabled": true
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `name` | string | yes | Friendly identifier |
| `destination_type` | string | no | `WEBHOOK` only (KAFKA, SNS — roadmap) |
| `webhook_url` | string | yes | HTTPS endpoint. Must respond 2xx within 10s |
| `webhook_secret` | string | no | HMAC-SHA256 signing key. **Saved write-only** — never echoed back |
| `matching_event_types` | string[] | no | Subscribe to specific events. Empty = subscribe to everything |
| `enabled` | bool | no | Default `true`. Set `false` to pause without deleting |
Valid event types:
- `send`, `delivered`, `bounce`, `complaint`
- `open`, `click`, `reject`
- `rendering_failure`, `delivery_delay`
## Response
```json
{
"success": true,
"destination_id": "ed_550e8400-...",
"name": "production-webhook",
"webhook_url": "https://yourapp.com/webhooks/email",
"matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"],
"enabled": true
}
```
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_.../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/email",
"webhook_secret": "...",
"matching_event_types": ["send", "delivered", "bounce", "complaint", "open", "click"]
}'
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 400 | `INVALID_REQUEST` | Missing `webhook_url` or unknown event type |
| 400 | `WEBHOOK_URL_INVALID` | URL must be HTTP(S) |
================================================================================
# Update event destination
Section: API Reference › Event Destinations
URL: https://email-docs.splashifypro.com/api-reference/event-destinations/update
================================================================================
> PATCH /api/v1/partner/email/configuration-sets/:id/event-destinations/:dest_id
# Update event destination
Modify a webhook destination — change the URL, rotate the signing
secret, narrow/widen subscribed event types, or pause delivery.
```http
PATCH /api/v1/partner/email/configuration-sets/:id/event-destinations/:dest_id
```
All fields optional — pass only what's changing.
## Request body
```json
{
"webhook_url": "https://yourapp.com/v2/webhooks/email",
"webhook_secret": "rotated-secret-here",
"matching_event_types": ["bounce", "complaint"],
"enabled": true
}
```
| Field | Type | Notes |
|---|---|---|
| `name` | string | |
| `webhook_url` | string | HTTP(S) only |
| `webhook_secret` | string | New value overwrites the old. API never echoes back |
| `matching_event_types` | string[] | Replaces the existing list |
| `enabled` | bool | Pause/resume delivery |
## Rotating the webhook secret
Sequence:
1. Generate a new secret on your side (`openssl rand -hex 32`)
2. Update your endpoint to **accept both old and new** secrets
for ~30 seconds
3. PATCH this endpoint with the new secret
4. Wait until in-flight requests drain (~30 seconds)
5. Remove old-secret support from your endpoint
We don't echo old-or-new values, so the rotation window has to
be coordinated by your code.
## cURL
```bash
curl -X PATCH \
https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_.../event-destinations/ed_... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
```
================================================================================
# Delete event destination
Section: API Reference › Event Destinations
URL: https://email-docs.splashifypro.com/api-reference/event-destinations/delete
================================================================================
> DELETE /api/v1/partner/email/configuration-sets/:id/event-destinations/:dest_id
# Delete event destination
Removes a webhook destination from a configuration set. Future
events for sends through this config set won't fan out to this
URL.
```http
DELETE /api/v1/partner/email/configuration-sets/:id/event-destinations/:dest_id
```
## cURL
```bash
curl -X DELETE \
https://api.splashifypro.com/api/v1/partner/email/configuration-sets/cs_.../event-destinations/ed_... \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## What this does NOT do
- Does NOT replay events your endpoint missed while the destination
was misconfigured. Manual replay is on the roadmap.
- Does NOT cancel in-flight retries — events that were queued
before the delete may still attempt delivery for ~21 minutes
(the retry window).
## Pause vs delete
If you want to temporarily stop delivery:
```bash
curl -X PATCH ... -d '{"enabled": false}'
```
is reversible. Deletion is not — re-creating gives you a fresh
`destination_id` and the historical webhook-attempts audit trail
no longer matches.
================================================================================
# List suppressed addresses
Section: API Reference › Suppression
URL: https://email-docs.splashifypro.com/api-reference/suppression/list
================================================================================
> GET /api/v1/partner/email/suppression
# List suppressed addresses
```http
GET /api/v1/partner/email/suppression
```
## Query parameters
| Field | Type | Notes |
|---|---|---|
| `reason` | string | `BOUNCE` / `COMPLAINT` / `UNSUBSCRIBE` / `MANUAL` |
| `search` | string | Case-insensitive substring match on email |
| `limit` | int | 1-1000, default 200 |
## Response
```json
{
"success": true,
"suppression": [
{
"email": "deadbox@example.com",
"reason": "BOUNCE",
"details": "550 5.1.1 user unknown",
"added_at": "2026-05-03T11:30:00Z"
},
{
"email": "spammed@example.com",
"reason": "COMPLAINT",
"details": "FBL/abuse",
"added_at": "2026-05-03T11:35:00Z"
}
],
"count": 2
}
```
## cURL
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/suppression?reason=BOUNCE&limit=50' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Get suppression entry
Section: API Reference › Suppression
URL: https://email-docs.splashifypro.com/api-reference/suppression/get
================================================================================
> GET /api/v1/partner/email/suppression/:email
# Get suppression entry
```http
GET /api/v1/partner/email/suppression/:email
```
Check whether a specific email is on the suppression list. Useful
for pre-flight checks before adding an address to a campaign list.
## Response
```json
{
"success": true,
"entry": {
"email": "deadbox@example.com",
"reason": "BOUNCE",
"details": "550 5.1.1 user unknown",
"added_at": "2026-05-03T11:30:00Z"
}
}
```
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 404 | `NOT_SUPPRESSED` | The email isn't on the suppression list — safe to send |
A 404 here is the **normal "not suppressed" response** — branch on
status code, not just body content.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/suppression/deadbox@example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Put suppression entry
Section: API Reference › Suppression
URL: https://email-docs.splashifypro.com/api-reference/suppression/put
================================================================================
> PUT /api/v1/partner/email/suppression/:email — add or replace a suppression.
# Put suppression entry
Idempotent — re-PUTting an existing email overwrites the reason +
details with the new values.
```http
PUT /api/v1/partner/email/suppression/:email
```
## Request body
```json
{
"reason": "MANUAL",
"details": "GDPR erasure request 2026-05-03 ticket #1024"
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `reason` | string | no | `MANUAL` (default) / `BOUNCE` / `COMPLAINT` / `UNSUBSCRIBE` |
| `details` | string | no | Free-text note. Surfaced in audit + UI |
## Response
```json
{
"success": true,
"email": "user@example.com",
"reason": "MANUAL"
}
```
## Use cases
- **Honoring opt-out clicks in your app** — when a user clicks
unsubscribe in your preferences page, push them here so you stop
sending across all your sending integrations
- **GDPR / DPDP erasure** — adds the address with a `MANUAL`
reason + a note referencing the ticket
- **Pre-emptive blocklist** — known-bad addresses you never want
to email, regardless of the source list
## cURL
```bash
curl -X PUT \
https://api.splashifypro.com/api/v1/partner/email/suppression/user@example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "MANUAL", "details": "User unsubscribed via app"}'
```
================================================================================
# Delete suppression entry
Section: API Reference › Suppression
URL: https://email-docs.splashifypro.com/api-reference/suppression/delete
================================================================================
> DELETE /api/v1/partner/email/suppression/:email — remove an address from the suppression list.
# Delete suppression entry
Removes an email from the suppression list, allowing future sends.
```http
DELETE /api/v1/partner/email/suppression/:email
```
> **Caution.** If the address bounced hard or complained, removing
> it and re-sending is likely to get it suppressed again + drag
> down your reputation. Only remove when you have evidence the
> underlying cause is fixed (e.g. user changed providers, mailbox
> was reactivated).
## cURL
```bash
curl -X DELETE \
https://api.splashifypro.com/api/v1/partner/email/suppression/user@example.com \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Response
```json
{ "success": true }
```
204 / 404 are both treated as success — if the address wasn't
suppressed, the desired end state is already achieved.
================================================================================
# Get quota
Section: API Reference › Quotas & Stats
URL: https://email-docs.splashifypro.com/api-reference/quotas/get-quota
================================================================================
> GET /api/v1/partner/email/quotas — your daily cap, peak rate, sandbox flag, reputation snapshot.
# Get quota
Single read-once-per-page summary of your account's sending budget
and reputation.
```http
GET /api/v1/partner/email/quotas
```
## Response
```json
{
"success": true,
"quota": {
"daily_send_quota": 50000,
"peak_send_rate_per_second": 14,
"sandbox": false,
"sent_today": 1247,
"sandbox_free_used_today": 0,
"reputation_status": "HEALTHY",
"reputation_bounce_rate": 0.0174,
"reputation_complaint_rate": 0.0003,
"sending_paused": false,
"sending_paused_reason": "",
"updated_at": "2026-05-03T12:00:00Z"
}
}
```
| Field | Notes |
|---|---|
| `daily_send_quota` | Max emails/day (200 in sandbox, 50K in production by default) |
| `peak_send_rate_per_second` | Max sends/sec (1 in sandbox, 14 in production) |
| `sandbox` | `true` until production access is approved |
| `sent_today` | Emails sent today (UTC) |
| `sandbox_free_used_today` | Emails consumed against the sandbox free tier |
| `reputation_status` | `HEALTHY` / `AT_RISK` / `PAUSED` |
| `reputation_bounce_rate` | Rolling 14-day bounce rate (0.0–1.0) |
| `reputation_complaint_rate` | Rolling 14-day complaint rate (0.0–1.0) |
| `sending_paused` | `true` if account is paused (admin or auto) |
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/quotas \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Bounce report
Section: API Reference › Quotas & Stats
URL: https://email-docs.splashifypro.com/api-reference/bounce-report
================================================================================
> GET /api/v1/partner/email/bounce-report: bounces by kind and by recipient domain over the last days.
# Bounce report
Why your emails bounced over the last days: totals, the main kinds of bounce and the recipient domains that bounce most. The same numbers as **Bounce report** in the panel.
```http
GET /api/v1/partner/email/bounce-report
```
Connected apps need `email.reports:read`. See [Connected apps (OAuth)](/api-reference/connected-apps).
## Query parameters
| Field | Type | Notes |
|---|---|---|
| `days` | int | 1 to 90, default 30 |
## Response
```json
{
"success": true,
"report": {
"sent": 18240,
"delivered": 17702,
"soft_bounces": 96,
"hard_bounces": 214,
"hard_bounce_rate": 1.17,
"complaints": 6,
"opens": 6120,
"clicks": 980,
"rejected": 22,
"by_category": [
{ "category": "User not found", "count": 171, "percentage": 55.2, "description": "Recipient mailbox doesn't exist. Remove these addresses from your list." },
{ "category": "Mailbox full", "count": 58, "percentage": 18.7, "description": "Recipient mailbox is over quota. Will likely accept mail later." }
],
"by_domain": [
{ "domain": "example.com", "count": 64, "percentage": 20.6 }
],
"window_days": 30
}
}
```
| Field | Meaning |
|---|---|
| `hard_bounces`, `soft_bounces` | Permanent bounces (remove the address) and temporary ones (often accepted later) |
| `hard_bounce_rate` | Hard bounces as a percent of `sent`. Keep it low to protect your sending reputation. |
| `by_category` | Bounces by kind, most first: `User not found`, `Mailbox full`, `Invalid domain`, `Spam / policy block`, `Connection issues`, `TLS / encryption`, `Others` |
| `by_domain` | The 10 recipient domains with the most bounces |
| `window_days` | The number of days counted |
`by_category` and `by_domain` are empty (or `null`) when nothing bounced.
For a file, `GET /api/v1/partner/email/bounce-report.csv` gives the same report as CSV (not open to connected apps).
## cURL
```bash
curl "https://api.splashifypro.com/api/v1/partner/email/bounce-report?days=7" \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Common errors
| Status | `message` | Why |
|---|---|---|
| 401 | `unauthorized` | Missing or wrong key or token |
| 500 | `database error` | Try again in a moment |
================================================================================
# Get send statistics
Section: API Reference › Quotas & Stats
URL: https://email-docs.splashifypro.com/api-reference/quotas/get-stats
================================================================================
> GET /api/v1/partner/email/stats — day-by-day send / delivered / bounced / opened / clicked counters.
# Get send statistics
Day-by-day counters across the rolling window. Account-wide by
default; pass `config_set_id` to scope to a specific configuration
set.
```http
GET /api/v1/partner/email/stats
```
## Query parameters
| Field | Type | Notes |
|---|---|---|
| `days` | int | 1-90, default 14 |
| `config_set_id` | uuid | Scope to one config set instead of account-wide |
## Response
```json
{
"success": true,
"stats": [
{
"day_bucket": "2026-05-03",
"send_count": 1247,
"delivered_count": 1213,
"bounced_count": 21,
"complained_count": 2,
"opened_count": 412,
"clicked_count": 87,
"rejected_count": 11
},
{
"day_bucket": "2026-05-02",
"send_count": 1102,
...
}
],
"days": 14
}
```
Days with zero sends are present in the array (with all counts
zero) so you don't have to fill gaps client-side.
## cURL
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/stats?days=30' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
Per-config-set:
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/stats?days=14&config_set_id=cs_...' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Get reputation
Section: API Reference › Quotas & Stats
URL: https://email-docs.splashifypro.com/api-reference/quotas/get-reputation
================================================================================
> GET /api/v1/partner/email/reputation — rolling 14-day bounce + complaint rate.
# Get reputation
Returns the rolling 14-day bounce + complaint rate plus the
classified reputation status. Equivalent to AWS SES
`GetAccountReputation`.
```http
GET /api/v1/partner/email/reputation
```
## Response
```json
{
"success": true,
"window_days": 14,
"sent": 12500,
"bounced": 218,
"complained": 4,
"bounce_rate": 0.01744,
"complaint_rate": 0.00032,
"status": "HEALTHY"
}
```
## Status thresholds
```
bounce > 10% OR complaint > 0.5% → PAUSED
bounce > 5% OR complaint > 0.1% → AT_RISK
else → HEALTHY
```
`PAUSED` triggers automatic sending halt — `/send` returns
`403 SENDING_PAUSED`. See [Reputation](/knowledge-base/concepts/reputation)
for recovery steps.
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/reputation \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# List events
Section: API Reference › Quotas & Stats
URL: https://email-docs.splashifypro.com/api-reference/quotas/list-events
================================================================================
> GET /api/v1/partner/email/events — recent event log.
# List events
Browse the recent event log. Useful for ad-hoc triage when you
want to see what's happening without setting up a webhook.
```http
GET /api/v1/partner/email/events
```
## Query parameters
| Field | Type | Notes |
|---|---|---|
| `day_bucket` | string | `YYYY-MM-DD` UTC. Default: today |
| `event_type` | string | Filter by event type (lowercase) |
| `limit` | int | 1-1000, default 200 |
## Response
```json
{
"success": true,
"events": [
{
"event_id": "e_550e8400-...",
"event_type": "delivered",
"message_id": "550e8400-...",
"config_set_id": "cs_...",
"recipient": "customer@example.com",
"created_at": "2026-05-03T12:34:57Z"
},
{
"event_id": "e_660e8400-...",
"event_type": "bounce",
"message_id": "660e8400-...",
"recipient": "deadbox@example.com",
"bounce_type": "Permanent",
"bounce_reason": "no_such_user",
"created_at": "2026-05-03T12:35:01Z"
},
{
"event_id": "e_770e8400-...",
"event_type": "click",
"message_id": "...",
"recipient": "...",
"url": "https://yourapp.com/dashboard",
"ip": "203.0.113.42",
"ua": "Mozilla/5.0 ...",
"created_at": "..."
}
],
"count": 3,
"day_bucket": "2026-05-03"
}
```
## cURL
```bash
curl 'https://api.splashifypro.com/api/v1/partner/email/events?day_bucket=2026-05-03&event_type=bounce&limit=100' \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
## Webhooks vs this endpoint
For push-based event delivery, use [webhooks](/webhooks). This
endpoint is for ad-hoc reads — debugging, auditing, replaying a
window of events into your own database.
Note: only the last 30 days of events are retained. For long-term
archival, persist them at your end via webhooks.
================================================================================
# Submit production access request
Section: API Reference › Production Access
URL: https://email-docs.splashifypro.com/api-reference/production-access/submit
================================================================================
> POST /api/v1/partner/email/production-access — request to leave sandbox.
# Submit production access request
Request your account be moved out of sandbox into full production.
Submission triggers an admin review. See
[Sandbox vs Production](/knowledge-base/concepts/sandbox) for the
review criteria.
```http
POST /api/v1/partner/email/production-access
```
## Request body
```json
{
"use_case": "We send transactional emails for our SaaS app — signup confirmations, password resets, payment receipts, weekly digest. Recipients are our paying customers who created accounts on our app.",
"email_volume_estimate": "5000-15000 per day",
"has_unsubscribe_method": true,
"has_consent_proof": true
}
```
| Field | Type | Required | Notes |
|---|---|---|---|
| `use_case` | string | yes | ≥30 chars. Real description of WHO you're sending to + WHY they signed up |
| `email_volume_estimate` | string | yes | Realistic daily volume |
| `has_unsubscribe_method` | bool | yes | Confirms you have unsubscribe links in marketing email |
| `has_consent_proof` | bool | yes | Confirms recipients opted in (signup, double-opt-in, purchase, etc.) |
| `legal_document` | file | no | Optional supporting document (PDF / PNG / JPEG, max 10 MB). See below |
All four data fields are required. Faking them violates AUP — accounts
that lie get downgraded.
## Optional legal document attachment
You can attach a supporting compliance document — CAN-SPAM attestation,
opt-in proof, signed contract, registered-business certificate, etc. —
to speed up the review. Submissions that include a relevant document
are typically approved faster because the reviewer doesn't need to
ask follow-up questions.
To attach a document, switch the request from JSON to
`multipart/form-data` and include the file under the `legal_document`
field. The file is uploaded to our DigitalOcean Spaces bucket and
exposed to the admin reviewer via a public URL on the request row.
**Limits:** PDF, PNG, or JPEG only. 10 MB max per file.
## Response
```json
{
"success": true,
"request_id": "req_550e8400-...",
"status": "PENDING",
"domain_verified": true,
"message": "Request submitted. Review typically takes 24 business hours.",
"legal_document_url": "https://folder.splashifypro.com/partner_email_production_access/...pdf",
"legal_document_filename": "compliance-attestation-2026.pdf"
}
```
`domain_verified` reflects whether you currently have at least one
domain identity in `VERIFIED` status — flagged on the admin side as
a "ready for fast approval" signal.
`legal_document_url` and `legal_document_filename` are returned only
when a document was uploaded with the request. Both fields are
omitted otherwise.
## Common errors
| Status | Code | Meaning |
|---|---|---|
| 400 | `USE_CASE_TOO_SHORT` | Description must be ≥30 characters |
| 400 | `MISSING_UNSUBSCRIBE_METHOD` | Required for marketing email |
| 400 | `MISSING_CONSENT_PROOF` | Recipients must have opted in |
| 400 | `INVALID_DOCUMENT_TYPE` | Document must be PDF, PNG, or JPEG |
| 400 | `DOCUMENT_TOO_LARGE` | Document must be 10 MB or smaller |
| 503 | `UPLOAD_UNAVAILABLE` | Spaces uploader is temporarily down — retry without the file |
## cURL — JSON (no document)
```bash
curl https://api.splashifypro.com/api/v1/partner/email/production-access \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"use_case": "...",
"email_volume_estimate": "5000 per day",
"has_unsubscribe_method": true,
"has_consent_proof": true
}'
```
## cURL — multipart with legal document
```bash
curl https://api.splashifypro.com/api/v1/partner/email/production-access \
-H "Authorization: Bearer $SPLASHIFY_API_KEY" \
-F "use_case=We send transactional emails for our SaaS app..." \
-F "email_volume_estimate=5000 per day" \
-F "has_unsubscribe_method=true" \
-F "has_consent_proof=true" \
-F "legal_document=@./compliance-attestation.pdf"
```
Note the absence of `Content-Type` — `curl -F` sets the multipart
boundary automatically.
================================================================================
# List production access requests
Section: API Reference › Production Access
URL: https://email-docs.splashifypro.com/api-reference/production-access/list
================================================================================
> GET /api/v1/partner/email/production-access — your submission history.
# List production access requests
Returns your full history of production-access submissions
newest-first.
```http
GET /api/v1/partner/email/production-access
```
## Response
```json
{
"success": true,
"requests": [
{
"request_id": "req_550e8400-...",
"use_case": "...",
"email_volume_estimate": "5000 per day",
"has_unsubscribe_method": true,
"has_consent_proof": true,
"domain_verified": true,
"status": "APPROVED",
"admin_notes": "Verified domain + clean use case. Approved for production.",
"created_at": "2026-05-03T12:00:00Z",
"reviewed_at": "2026-05-03T13:30:00Z"
}
],
"count": 1
}
```
## Status values
| Status | Meaning |
|---|---|
| `PENDING` | Awaiting review |
| `APPROVED` | Lifted out of sandbox + quota raised |
| `DENIED` | Resubmit after addressing `admin_notes` |
## cURL
```bash
curl https://api.splashifypro.com/api/v1/partner/email/production-access \
-H "Authorization: Bearer $SPLASHIFY_API_KEY"
```
================================================================================
# Webhooks
Section: API Reference › Webhooks
URL: https://email-docs.splashifypro.com/webhooks
================================================================================
> Receive real-time events when emails are sent, delivered, bounced, opened, clicked, or replied to.
# 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:
| Surface | Where you set it | Scope | Best for |
|---|---|---|---|
| **Account-level webhook** | Settings → Webhook URL in Splashify Pro Email | Every event for every send + lifecycle events (production-access approval, account deletion, etc) | Single endpoint that handles everything; simplest setup |
| **Per-config-set destinations** | `POST /partner/email/configuration-sets/:id/event-destinations` | Only events scoped to that config set, filtered by `matching_event_types` | Per-customer or per-product routing; advanced filtering |
**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.
## Event types fired to your webhook
| Event | When | Surface |
|---|---|---|
| `email.sent` | Recipient MX accepted the message (250 OK) | both |
| `email.delivered` | Same as sent — direct-MX collapses these | both |
| `email.bounced` | Hard or soft bounce reported via DSN | both |
| `email.complained` | Recipient marked as spam (FBL) | both |
| `email.opened` | Recipient opened the email (1×1 pixel loaded) | both |
| `email.clicked` | Recipient clicked a link in the email | both |
| `email.rejected` | Send refused at the API layer (suppression / quota) | both |
| `email.replied` | **Recipient replied to the email** | both |
| `production_access.approved` | Admin lifted your sandbox cap | account-level only |
| `production_access.denied` | Admin denied your sandbox lift request | account-level only |
| `account.deletion.requested` | You requested account deletion | account-level only |
| `account.deletion.cancelled` | You cancelled the deletion within the 48h window | account-level only |
## How it works
```mermaid
sequenceDiagram
participant API as Splashify API
participant Worker as Webhook dispatcher
participant Hook as Your endpoint
API->>Worker: Event recorded
Worker->>Worker: Find matching destination
(by config_set + event type)
Worker->>Hook: POST signed JSON
alt 2xx
Worker->>Worker: mark delivered
else 5xx / timeout
Worker->>Worker: schedule retry
(1m, 5m, 15m)
else 4xx
Worker->>Worker: gave up
(your URL is broken)
end
```
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": "hello@yourcompany.com",
"to": ["customer@example.com"],
"subject": "Welcome",
"html_body": "Hi
",
"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": "hello@yourcompany.com",
"destination": ["customer@example.com"]
},
"delivery": {
"timestamp": "2026-05-03T12:34:57Z",
"recipients": ["customer@example.com"],
"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:
| Header | Purpose |
|---|---|
| `Content-Type` | `application/json` |
| `User-Agent` | `Splashify-Pro-Webhook/1.0` |
| `X-Splashify-Event` | Event type — `Send`, `Delivery`, `Bounce`, ... |
| `X-Splashify-Signature` | `sha256=` HMAC-SHA256 of the raw body keyed by your `webhook_secret` |
| `X-Splashify-Timestamp` | Unix seconds — protects against replay |
| `X-Splashify-Delivery-ID` | UUID per delivery attempt — use for idempotency |
## Quick verify in Node
```js
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
- [**Event Types →**](/webhooks/event-types) — full payload shape per event
- [**Verify Signature →**](/webhooks/verify-signature) — implementations in 6 languages
- [**Retries & Replays →**](/webhooks/retries) — backoff schedule + manual replay
- [**Best Practices →**](/webhooks/best-practices) — idempotency, dedup, security
================================================================================
# Event Types
Section: API Reference › Webhooks
URL: https://email-docs.splashifypro.com/webhooks/event-types
================================================================================
> Every webhook event Splashify Pro fires, with full payload examples.
# Event Types
The Email API fires nine event types. Subscribe to all of them or
just the ones you care about via the `matching_event_types` array
on the destination config.
| Event | When it fires | Header value |
|---|---|---|
| [`Send`](#send) | We accept the API call + queue the email | `Send` |
| [`Delivery`](#delivery) | Recipient MX returns 250 OK | `Delivery` |
| [`Bounce`](#bounce) | Hard or soft bounce | `Bounce` |
| [`Complaint`](#complaint) | Recipient marks as spam (FBL report) | `Complaint` |
| [`Open`](#open) | Recipient opens the email (pixel loaded) | `Open` |
| [`Click`](#click) | Recipient clicks a link in the email | `Click` |
| [`Reject`](#reject) | We refused to send (suppression list, etc.) | `Reject` |
| [`RenderingFailure`](#renderingfailure) | Template variable substitution failed | `RenderingFailure` |
| [`DeliveryDelay`](#deliverydelay) | Soft bounce — will retry | `DeliveryDelay` |
Every payload starts with the same envelope:
```json
{
"eventType": "",
"mail": {
"timestamp": "ISO8601",
"messageId": "uuid",
"source": "from-address",
"destination": ["to-address"]
},
"": { ... }
}
```
Below: the event-specific block for each type.
## Send
Fires when the API accepts your call. The email is now in the queue
— no MX attempt yet.
```json
{
"eventType": "Send",
"mail": {
"timestamp": "2026-05-03T12:34:56.123Z",
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"source": "hello@yourcompany.com",
"destination": ["customer@example.com"]
},
"send": {}
}
```
## Delivery
Fires when the recipient's mailbox provider accepts the email
(250 OK). This is the strongest delivery signal — the receiving
server has accepted the message for the recipient's mailbox.
```json
{
"eventType": "Delivery",
"mail": { ... },
"delivery": {
"timestamp": "2026-05-03T12:34:57.456Z",
"recipients": ["customer@example.com"],
"smtpResponse": "250 OK"
}
}
```
## Bounce
Hard bounce (`Permanent`) means the address is dead — recipient is
auto-added to your suppression list. Soft bounce (`Transient`) means
mailbox-full / server-down / etc. — we retry up to 3 times before
giving up.
```json
{
"eventType": "Bounce",
"mail": { ... },
"bounce": {
"timestamp": "2026-05-03T12:34:58.789Z",
"bounceType": "Permanent",
"bounceSubType": "no_such_user",
"bouncedRecipients": [
{
"emailAddress": "deadbox@example.com",
"diagnosticCode": "550 5.1.1 user unknown"
}
]
}
}
```
## Complaint
Recipient marked the email as spam. We received the FBL (feedback-
loop) report from their inbox provider and auto-suppressed the
address. **Complaints are critical to monitor** — high complaint
rates trigger reputation-based sending pauses.
```json
{
"eventType": "Complaint",
"mail": { ... },
"complaint": {
"timestamp": "2026-05-03T12:35:10.123Z",
"complaintFeedbackType": "abuse",
"complainedRecipients": [
{ "emailAddress": "customer@example.com" }
]
}
}
```
## Open
Recipient opened the email — the 1×1 tracking pixel loaded. Privacy-
focused mail clients (Apple Mail, etc.) pre-load the pixel
proactively, so opens are a directional signal, not an exact one.
```json
{
"eventType": "Open",
"mail": { ... },
"open": {
"timestamp": "2026-05-03T12:36:00.000Z",
"ipAddress": "203.0.113.42",
"userAgent": "Mozilla/5.0 (iPhone; ...)"
}
}
```
## Click
Recipient clicked a tracked link. We rewrite every `` in the
HTML body through a redirect proxy that records the click + 302s to
the original URL. Unsubscribe links are NOT tracked.
```json
{
"eventType": "Click",
"mail": { ... },
"click": {
"timestamp": "2026-05-03T12:37:00.000Z",
"ipAddress": "203.0.113.42",
"userAgent": "Mozilla/5.0 (Macintosh; ...)",
"link": "https://yourapp.com/dashboard"
}
}
```
## Reject
We refused to send. Common reasons: recipient on suppression list,
sandbox limit reached, sending paused.
```json
{
"eventType": "Reject",
"mail": { ... },
"reject": {
"reason": "address on suppression list"
}
}
```
## Reply
A recipient replied to one of your emails. We catch replies via a
unique `Reply-To: reply+@mail.splashifypro.com` we stamp on
every outbound (token encodes the original `outbox_id`). When the
reply lands at our inbound listener, we parse it, look up which
outbound it's responding to, and fire this event.
```json
{
"eventType": "Reply",
"mail": { ... },
"reply": {
"outbox_id": "f8c5def1-1234-5678-9abc-def012345678",
"original_recipient": "customer@example.com",
"from": "Customer Name ",
"subject": "Re: Your order has shipped",
"message_id": "",
"in_reply_to": "",
"references": "",
"text_body": "Thanks! When can I expect delivery?...",
"html_body": "Thanks! When can I expect...",
"received_at": "2026-05-04T09:15:30.123Z"
}
}
```
`text_body` is truncated to 32 KB and `html_body` to 64 KB — long
quoted-thread replies stay inside the webhook envelope without
bloating it. Use `in_reply_to` + `references` for thread correlation
on your side.
**Disabling reply capture.** If you'd rather replies go directly to
the address in your `From` header (or your own `Reply-To` if you
set one), pass `Reply-To: your-inbox@yourcompany.com` on your send
request — when the field is non-empty we won't override it. Replies
then route directly to your address and we never see them.
> Replies that were already in flight when you change the setting
> still arrive at the original `reply+` address until the
> recipient updates their thread.
## RenderingFailure
Template variable substitution failed — `{{variable}}` referenced
in template body wasn't supplied at send time, OR was malformed.
```json
{
"eventType": "RenderingFailure",
"mail": { ... },
"renderingFailure": {
"templateName": "welcome",
"errorMessage": "missing required variable: first_name"
}
}
```
## DeliveryDelay
Soft bounce — we'll retry. Fires once on the first retry-eligible
soft bounce so you can surface a "delivery delayed" UI without
waiting for the final outcome.
```json
{
"eventType": "DeliveryDelay",
"mail": { ... },
"deliveryDelay": {
"timestamp": "2026-05-03T12:34:59.000Z",
"delayType": "TemporaryFailure",
"delayedRecipients": ["customer@example.com"]
}
}
```
## Subscribing to a subset
Want only bounces and complaints? Set `matching_event_types` on the
destination:
```bash
curl ... -d '{
"name": "deliverability-alerts",
"destination_type": "WEBHOOK",
"webhook_url": "https://yourapp.com/webhooks/deliverability",
"webhook_secret": "...",
"matching_event_types": ["bounce", "complaint"]
}'
```
Empty array = subscribe to everything. Specific list = events not
on the list are skipped.
## Multiple destinations per config set
You can attach multiple webhook destinations to a single config set
— one for engagement events (open / click), one for deliverability
alerts (bounce / complaint), one for archival (everything). Each
destination delivers + retries independently.
================================================================================
# Open & click tracking
Section: API Reference › Webhooks
URL: https://email-docs.splashifypro.com/webhooks/tracking
================================================================================
> Control which sends generate Open and Click events on your webhook.
# Open & click tracking
Every email sent through Splashify Pro can carry two kinds of
recipient-side instrumentation:
| Tracker | Mechanism | Generates |
|---|---|---|
| **Open** | 1×1 transparent GIF embedded just before `