₹0.15/SMS on Business planFree DLT registration helpInternational SMS worldwide
Talk to sales+91 95851 60363 Get started
Email API

Email API for Developers: Send, Track and Manage Email

One REST API to send single emails or batches of up to 1,000, with templates, attachments, scheduling and HMAC-signed webhooks. If you already use our SMS API, the same key works for email.

REST API, JSONHMAC-signed webhooksSame key as SMS API
Developer writing code on a laptop
✓✓
Deliveredemail.delivered event received
{}
202 Acceptedmessage_id em_8f2c
Quick answer

Our email API is a REST interface at /api/v2/email. You POST to /send with a From address on your verified domain, recipients, a subject and HTML, text or a template ID. It replies with HTTP 202 and a message ID per recipient. Delivery, bounce and complaint events are posted to your webhook, signed with HMAC SHA-256.

1,000recipients per batch call
202accepted, with message IDs
4 triesper webhook event

Get pricing & plans

We'll call within working hours with the right plan and check your DLT or sender ID status.

Code samples

Send your first email from code

POST JSON to /email/send with your API key. The reply is HTTP 202 with a message ID per recipient. Delivery events arrive at your webhook, signed with HMAC SHA-256.

# Send one transactional email from your verified domain
curl -X POST "https://whitelabled.securesmsc.com/api/v2/email/send" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-KM2431-invoice" \
  -d '{"from": {"email": "billing@kovaimart.in", "name": "Kovai Mart"},
       "to": "priya@example.in",
       "subject": "Invoice KM/2431 for your order",
       "template_id": "tpl_12",
       "variables": {"name": "Priya", "order": "KM2431", "amount": "2,360"},
       "category": "transactional"}'
$ch = curl_init("https://whitelabled.securesmsc.com/api/v2/email/send");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => [
    "Authorization: Bearer " . getenv("SMS_API_KEY"),
    "Content-Type: application/json",
    "Idempotency-Key: order-KM2431-invoice",
  ],
  CURLOPT_POSTFIELDS => json_encode([
    "from" => ["email" => "billing@kovaimart.in", "name" => "Kovai Mart"],
    "to" => "priya@example.in",
    "subject" => "Invoice KM/2431 for your order",
    "html" => "<p>Hi Priya, your GST invoice for order KM2431 is attached.</p>",
    "attachments" => [["filename" => "KM2431.pdf", "content_type" => "application/pdf",
                       "content" => base64_encode(file_get_contents("KM2431.pdf"))]],
    "category" => "transactional",
  ]),
]);
$res = json_decode(curl_exec($ch), true);
echo $res["data"]["messages"][0]["message_id"] ?? $res["code"];
const res = await fetch("https://whitelabled.securesmsc.com/api/v2/email/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SMS_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "order-KM2431-invoice",
  },
  body: JSON.stringify({
    from: { email: "billing@kovaimart.in", name: "Kovai Mart" },
    to: "priya@example.in",
    subject: "Invoice KM/2431 for your order",
    html: "<p>Hi Priya, your GST invoice for order KM2431 is attached.</p>",
    category: "transactional",
  }),
});
const { data } = await res.json();   // HTTP 202
console.log(data.messages[0].message_id);
import hmac, hashlib, os, requests

r = requests.post(
    "https://whitelabled.securesmsc.com/api/v2/email/send",
    headers={"Authorization": f"Bearer {os.environ['SMS_API_KEY']}",
             "Idempotency-Key": "order-KM2431-invoice"},
    json={"from": "billing@kovaimart.in", "to": "priya@example.in",
          "subject": "Invoice KM/2431 for your order",
          "html": "<p>Hi Priya, your GST invoice for order KM2431 is attached.</p>", "category": "transactional"},
    timeout=15,
)
print(r.status_code, r.json()["data"]["messages"][0]["message_id"])

# Verify a webhook event (Flask-style request object)
def valid(req, secret):
    ts = req.headers["X-SMSC-Timestamp"]
    mac = hmac.new(secret.encode(), f"{ts}.".encode() + req.get_data(), hashlib.sha256).hexdigest()
    return hmac.compare_digest("sha256=" + mac, req.headers["X-SMSC-Signature"])
Base URL https://whitelabled.securesmsc.com/api/v2. Email is enabled per account.
Errors

Common error codes

Email API error codes
HTTPCodeWhat it means
400invalid_jsonThe body is not valid JSON.
402insufficient_creditsEmail credit balance is too low for this send.
403email_not_enabledEmail is not switched on for the account yet.
422from_domain_not_verifiedThe From domain is not verified. Publish its DNS records and verify it.
422too_many_recipientsMore than 50 recipients on /send. Use /batch.
422all_recipients_suppressedEvery recipient is on your suppression list. Nothing was sent or charged.
429rate_limited / daily_limit_reachedSlow down and retry later, or ask us to review your limits.
503no_route_availableTemporary. Retry with back-off.

Send or batch

Up to 50 recipients per send call, or 1,000 with personal variables per batch.

Idempotent requests

Pass an Idempotency-Key header so a retry never sends the same email twice.

Signed webhooks

Verify every event with the X-SMSC-Signature header and your webhook secret.

Manage it all by API

Domains, templates, suppressions, balance and message search have their own endpoints.

Authentication and your first request

Send your API key as a Bearer token in the Authorization header. It is the same key as the SMS API, created in the panel. Email is switched on per account, so ask us to enable it, then verify your sending domain before your first call.

POST a JSON body to /api/v2/email/send. The required fields are from, to and subject, plus html, text or template_id. Add category set to transactional or marketing, reply_to, cc and bcc, tags, metadata and custom headers as needed. A successful call returns HTTP 202 with a batch ID, the credits charged and a message ID for each recipient.

Templates, batches and attachments

Save templates through the API or the panel and send with template_id plus a variables object. Use double braces such as {{name}} for values that should be HTML-escaped, and triple braces for trusted HTML. For campaigns or digests, POST to /batch with up to 1,000 recipients, each with their own variables and metadata.

Attachments go in the attachments array as base64 with a filename and content type, up to 10 files of 2 MB each and 5 MB in total. Mark an image as inline with a content ID to show it in the body. To send later, set send_at with a timezone, up to 30 days ahead. You can cancel a scheduled message or batch.

Developer writing code on a laptop

Webhook events

Set one webhook URL for your account, or pass webhook_url on a single send. By default we post email.delivered, email.bounced, email.complained, email.failed and email.rejected. You can also subscribe to deferred, opened, clicked and unsubscribed events.

Each POST carries an X-SMSC-Signature header: an HMAC SHA-256 of the timestamp and the raw body, keyed with your webhook secret. Reject any event whose signature does not match or whose timestamp is old. If your server does not answer with a 2xx status, we retry after 10 seconds, 1 minute and 5 minutes, so make your handler idempotent on the event ID.

Errors and limits

Errors return a JSON body with a machine-readable code. The ones you will meet most are from_domain_not_verified, insufficient_credits, too_many_recipients, all_recipients_suppressed, rate_limited and daily_limit_reached. Treat 429 and 503 responses as temporary and retry with back-off. Treat 422 responses as permanent and fix the request.

New accounts start with a daily limit and a per-minute limit that rise as your sending history builds. Check your email credit balance with GET /api/v2/email/balance before a large batch.

Domains and suppressions by API

Add and verify sending domains with the /domains endpoints. The response lists the DNS records to publish, and POST /domains/{domain}/verify re-checks them on demand. Free-mail domains cannot be added.

The /suppressions endpoints list, add and remove suppressed addresses, so you can sync unsubscribes from your CRM. POST /validate checks a list of addresses before you send, at one credit per address.

Get started

How it works

Enable email

Ask us to switch email on for your account and agree a rate for your volume.

Verify a domain

Add it with POST /domains, publish the records, then call the verify endpoint.

Send a test

POST to /send with your key and check the message ID and webhook event.

Go live

Move templates to the API, add the idempotency key and watch your error codes.

FAQ

Frequently asked questions

What is an email API?

An email API lets your software send email with an HTTPS request instead of connecting to a mail server. You send the recipient, subject and content as JSON, and the API returns a message ID. Delivery results come back as webhook events, so your app knows which emails arrived, bounced or were marked as spam.

Is the email API key the same as the SMS API key?

Yes. One API key works for the SMS and email APIs on the same account, sent as a Bearer token. Email has its own credit balance, which you can check with the balance endpoint. A key is locked to the server IP addresses allowed for it, so if email will go out from a different server, ask us to add that IP.

How do I verify webhook signatures?

Each webhook request has X-SMSC-Timestamp and X-SMSC-Signature headers. Compute an HMAC SHA-256 of the timestamp, a dot and the raw request body, using your webhook secret, and compare it with the signature value after sha256=. Reject requests that do not match. You can rotate the secret through the API.

Can I send email over SMTP instead?

Not at present. Sending is through the REST API only, which gives you message IDs, idempotency, scheduling and signed events that SMTP cannot return. If an older system can only send over SMTP, talk to us about the options before you plan the integration.

How many emails can one API call send?

A call to /send can address up to 50 recipients, counting cc and bcc. For larger sends, use /batch with up to 1,000 recipients, each with their own variables. Attachments are allowed only on smaller sends. New accounts also have a daily limit that rises over time.

What happens when an address bounces?

A hard bounce adds the address to your suppression list, and later sends to it are skipped and not charged. Repeated soft bounces suppress an address for a few days. You receive an email.bounced webhook with the bounce type, so you can update the customer record in your own system.

Get email enabled on your API key

Tell us your volume and sending domain. We switch email on, and your existing SMS key starts working for email.

Chat on WhatsApp