Getting Started
The Bulk Email API lets you send transactional email, launch bulk campaigns to recipient groups, and receive delivery-status webhooks — all from your own systems, without the console UI.
- Sign in to the Bulk Email console and open Developer API in the sidebar.
- Click Generate key to issue your account's shared API key.
- Send it as the
x-api-keyheader on every request below.
Authentication
Your API key is the same credential your SMS and WhatsApp Business integrations already use — one key per intouchvas account, issued and held by identity-service, not something separate per product.
x-api-key: <your key>
Set up a sending domain
By default every send goes out from a shared intouchvas address. To send as you@yourdomain.com instead, register and verify your own domain first — the API rejects any fromEmail/from_email whose domain isn't verified on your account.
/sender-domains
curl -X POST https://bulkemail-service.intouchvas.io/api/v1/sender-domains \
-H "x-api-key: <your key>" \
-H "Content-Type: application/json" \
-d '{"domain": "yourdomain.com"}'
Response — 201
{
"id": 2,
"domain": "yourdomain.com",
"status": "pending",
"dkim_tokens": ["n5m2d7yzzjpf4zvbok6weyiq5gsfj767", "lzpsjh2ar5m4nedwj2pp5lgs63zkzqdq", "vq45eygudpsztyzgli2hgj5n5angsyx2"],
"dkim_records": [
{
"type": "CNAME",
"name": "n5m2d7yzzjpf4zvbok6weyiq5gsfj767._domainkey.yourdomain.com",
"value": "n5m2d7yzzjpf4zvbok6weyiq5gsfj767.dkim.amazonses.com"
},
{ "type": "CNAME", "name": "...", "value": "..." },
{ "type": "CNAME", "name": "...", "value": "..." }
],
"created": "2026-10-15T12:01:52Z"
}
Add the DNS records
Add all three dkim_records entries to your domain's DNS as CNAME records — name is the record name, value is what it points to. This proves domain ownership and lets us sign outgoing mail with a valid DKIM signature; it's purely additive and doesn't touch your MX records, SPF, or any other DKIM selector your domain already has for its normal mail.
Check status
Once the DNS records are live, check verification status — SES usually confirms within minutes, occasionally up to 72 hours depending on your DNS provider's propagation time.
/sender-domains/:id/verify
curl -X POST https://bulkemail-service.intouchvas.io/api/v1/sender-domains/2/verify \
-H "x-api-key: <your key>"
{
"id": 2,
"domain": "yourdomain.com",
"status": "verified",
"last_checked_at": "2026-10-15T12:14:30Z"
}
There's no background poller — status only updates when you call this endpoint, so poll it (e.g. every few minutes) until status flips from pending to verified. GET /sender-domains lists every domain on your account with its current status without re-checking SES.
Once verified, any address @yourdomain.com can be used as fromEmail/from_email — e.g. billing@yourdomain.com, no-reply@yourdomain.com — with no further setup.
Restrict allowed senders
By default a verified domain is open — any address at that domain can send. To lock it down to specific addresses only, add them to the allowlist; once at least one address is added, every other address on that domain is rejected.
/sender-domains/:id/addresses
curl -X POST https://bulkemail-service.intouchvas.io/api/v1/sender-domains/2/addresses \
-H "x-api-key: <your key>" \
-H "Content-Type: application/json" \
-d '{"email": "billing@yourdomain.com"}'
Send a transactional email
Send a single email, outside any campaign — a receipt, a password reset, a notification.
/send
Request body
| Field | Required | Description |
|---|---|---|
| to | required | Recipient email address. |
| subject | required | Email subject line. |
| body | required | HTML body. |
| fromName | optional | Display name for the From address. |
| fromEmail | optional | Defaults to your account's shared sender. If set, its domain must already be a verified Sender Domain on your account. |
| callbackUrl | optional | Where to POST delivery-status events for this send — see Delivery callbacks. |
curl -X POST https://bulkemail-service.intouchvas.io/api/v1/send \
-H "x-api-key: <your key>" \
-H "Content-Type: application/json" \
-d '{
"to": "recipient@example.com",
"subject": "Your receipt",
"body": "<p>Thanks for your order.</p>",
"fromName": "intouchvas",
"callbackUrl": "https://you.example.com/hooks/email"
}'
const res = await fetch('https://bulkemail-service.intouchvas.io/api/v1/send', {
method: 'POST',
headers: {
'x-api-key': '<your key>',
'Content-Type': 'application/json',
},
body: JSON.stringify({
to: 'recipient@example.com',
subject: 'Your receipt',
body: '<p>Thanks for your order.</p>',
fromName: 'intouchvas',
callbackUrl: 'https://you.example.com/hooks/email',
}),
});
const data = await res.json();
import requests
response = requests.post(
"https://bulkemail-service.intouchvas.io/api/v1/send",
headers={"x-api-key": "<your key>"},
json={
"to": "recipient@example.com",
"subject": "Your receipt",
"body": "<p>Thanks for your order.</p>",
"fromName": "intouchvas",
"callbackUrl": "https://you.example.com/hooks/email",
},
)
Response — 200
{
"status": "success",
"message": "Queued",
"messageId": 93201
}
Response — 403, recipient suppressed
{
"error": "recipient has unsubscribed or previously bounced"
}
Launch a campaign
Launch an already-designed campaign — subject, body, and recipient groups already attached via the console or the Campaigns endpoints — to its recipients. Validates, prices the send, publishes to the queue, and returns 202 immediately; it does not wait for any individual email to go out.
/campaigns/:id/send
curl -X POST https://bulkemail-service.intouchvas.io/api/v1/campaigns/42/send \
-H "x-api-key: <your key>"
Response — 202
{
"message": "campaign queued for sending",
"campaign_id": 42,
"recipient_count": 4820
}
Response — 400, no recipients
{
"error": "campaign has no recipients"
}
Delivery callbacks
Get notified the moment a send is delivered, bounces, is marked as spam, or is opened/clicked — instead of polling for status. Set a callback URL on the send itself:
POST /send— thecallbackUrlfield, per send.- Campaigns — a
callback_urlfield onPOST /campaignsorPUT /campaigns/:id, applied to every recipient in that campaign.
Callback payload
We POST this JSON body to your URL for each event, as it happens:
{
"event": "delivered",
"sent_email_id": 93201,
"campaign_id": 42,
"to_email": "recipient@example.com",
"status": "delivered",
"occurred_at": "2026-10-15T14:43:05Z"
}
| Field | Description |
|---|---|
| event | delivered · bounced · complained · opened · clicked |
| campaign_id | Omitted for an email sent via /send (no parent campaign). |
Errors
Errors are returned as {"error": "message"} with a relevant HTTP status code.
| Status | Meaning |
|---|---|
| 400 | Bad request — missing/invalid fields, or a validation failure (e.g. an empty campaign). |
| 401 | Missing or invalid API key. |
| 403 | Your key lacks permission for this action, or the recipient has unsubscribed/bounced previously. |
| 404 | The campaign or resource doesn't exist on your account. |
| 500 | Something went wrong on our side — safe to retry. |