API docs/Email API

Email API

Send OTPs, fee receipts with PDFs, reminders, welcome mail and newsletters from your own software with one HTTPS call, from your own domain, delivered through Amazon SES. Retries, bounces, unsubscribes and tracking are handled for you.

Base URL
https://msg.sisplerp.com/mail
Authentication
Authorization: Bearer sk_live_…

Get your keys in the panel: Email → Keys & API. Create a free account to start with free emails.

Keys

KeyUse it forNever
sk_live_…Everything, from your serverPut it in a web page or mobile app
pk_live_…The website contact form only (/v1/contact). Safe in page source: it can only send enquiries to you, from your own websites—
sk_test_… / pk_test_…Building and testing. Every check runs, nothing is sent, no credits are used. Send to bounce@sispl.test or complaint@sispl.test to see those outcomesExpect real delivery

Quick start

Send a one-time password with the ready-made otp template:

cURL
curl https://msg.sisplerp.com/mail/v1/send \
  -H "Authorization: Bearer $SISPL_MAIL_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "parent@example.com", "template": "otp", "vars": {"code": "482913"}}'
PHP
$ch = curl_init('https://msg.sisplerp.com/mail/v1/send');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('SISPL_MAIL_KEY'), 'Content-Type: application/json'],
  CURLOPT_POSTFIELDS => json_encode(['to' => 'parent@example.com', 'template' => 'otp', 'vars' => ['code' => '482913']]),
]);
$out = json_decode(curl_exec($ch), true);   // ['ok' => true, 'id' => 8812, 'status' => 'queued']
Node 18+
const res = await fetch('https://msg.sisplerp.com/mail/v1/send', {
  method: 'POST',
  headers: { Authorization: `Bearer ${process.env.SISPL_MAIL_KEY}`, 'Content-Type': 'application/json' },
  body: JSON.stringify({ to: 'parent@example.com', template: 'otp', vars: { code: '482913' } }),
});
const out = await res.json();   // { ok: true, id: 8812, status: "queued" }

A 202 means the email is queued. It goes out within seconds and is retried automatically if the receiving server has a problem.

Sending an email: POST /v1/send

FieldWhat it is
toRequired. One address or a list (up to 50 recipients in total with cc and bcc)
template + varsA ready-made template or one of yours, with its values. Or use subject with html / text
subject, html, textYour own email. text is made from html when you leave it out
cc, bccEach address is charged a credit
attachmentsUp to 5 files, 5 MB in total: [{ filename, contentType, content (base64) }]
fromAddressAnother address on your verified domain, e.g. accounts@yourschool.in
fromName, replyToSender name and reply address for this email only
dedupeKeySend the same key again and the email is not sent twice; comes back in webhooks as reference
sendAtSend later, e.g. 2026-10-08T09:00:00+05:30
priorityhigh jumps the queue: use it for OTPs
track{ "opens": true, "clicks": true } to track this email

Ready-made templates

Use these by name, in your brand colour and organisation name. You can also create your own in the panel.

TemplateValues
otpcode, appLabel, purpose, expiryMinutes
password-resetcode or resetUrl, appLabel, expiryMinutes
verify-emailverifyUrl, name, code, appLabel
welcomename, appLabel, username, loginUrl
fee-receiptreceiptNo, studentName, amount, paidOn, paymentMode
receiptreceiptNo, amount, paidOn, customerName
remindertitle, message, name, dueDate, amount
noticesubject, heading, intro, bodyHtml, actionUrl

Common tasks

Fee receipt with the PDF attached

JSON
{ "to": "parent@example.com", "template": "fee-receipt",
  "vars": { "receiptNo": "RCP-5001", "studentName": "Aarav Kumar", "amount": "4,500", "paidOn": "25 Sep 2026", "paymentMode": "UPI" },
  "dedupeKey": "fee-receipt:RCP-5001",
  "attachments": [ { "filename": "RCP-5001.pdf", "contentType": "application/pdf", "content": "<base64>" } ] }

The same message to many people: POST /v1/messages/batch

JSON
{ "template": "reminder",
  "messages": [
    { "to": "a@example.com", "vars": { "title": "Fee due", "message": "₹4,500 due by 10 Oct." } },
    { "to": "b@example.com", "vars": { "title": "Fee due", "message": "₹3,200 due by 10 Oct." } } ] }

Up to 1,000 messages a call. Every message is checked first; if one is wrong, nothing is sent or charged.

A contact form on any website, no backend

HTML
<form data-sispl-form data-key="pk_live_…">
  <input name="name" placeholder="Your name" required>
  <input name="email" type="email" placeholder="Email" required>
  <textarea name="message" placeholder="Message" required></textarea>
  <button type="submit">Send</button>
  <div data-sispl-status></div>
</form>
<script src="https://msg.sisplerp.com/mail/sispl-form.js" defer></script>

Enquiries arrive in your inbox with the visitor as Reply-To, and are kept in the panel. Spam is filtered without a CAPTCHA.

Sending safely twice

If a request times out, send it again with the same dedupeKey (a receipt or order number). It is delivered once; the repeat answers "status": "duplicate" and is not charged.

SMTP

For software that can only send through a mail server: WordPress, Tally Prime, PHPMailer, older ERPs and scanners.

SettingValue
Serversmtp.sisplerp.com
Port587 with STARTTLS (called "TLS" in most apps), or 465 with SSL/TLS
UsernameYour account name, shown on Email → Keys & API
PasswordA secret key created as an SMTP password in the panel

Your own domain

Add your domain on Email → Settings, then add the three DKIM records (and the recommended MAIL FROM and DMARC records) that the panel shows. Once verified, email goes out as office@yourschool.in, signed with DKIM, and any address on the domain can be used as fromAddress. Until then, email is sent from our address with replies coming to you.

Tracking and webhooks

GET /v1/messages/{id} shows an email's state: queued, sent, delivered, opened, clicked, or bounced, complained, failed. GET /v1/stats gives delivery, bounce and open rates. Set a webhook to be told instead:

cURL
curl -X PUT https://msg.sisplerp.com/mail/v1/webhook -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://erp.example.com/hooks/mail"}'
# → {"secret": "whsec_…"}   store it; it is shown once

Events: message.delivered, message.bounced, message.complained, message.failed, message.opened, message.clicked, message.unsubscribed, message.delayed, campaign.completed, domain.verified, domain.failed. Each request is signed: X-SISPL-Signature: t=<unix seconds>,v1=<HMAC-SHA256(secret, "t.body")>.

PHP: check the signature
function sispl_verify($secret, $body, $header) {
  $p = [];
  foreach (explode(',', $header) as $kv) { [$k, $v] = array_pad(explode('=', trim($kv), 2), 2, ''); $p[$k] = $v; }
  if (!isset($p['t'], $p['v1']) || abs(time() - (int) $p['t']) > 300) return false;
  return hash_equals(hash_hmac('sha256', $p['t'] . '.' . $body, $secret), $p['v1']);
}

Errors, credits and limits

StatusMeans
400The request is wrong; error, message and field say how
401Missing, unknown or revoked key
402Not enough credits: top up in the panel, then send again
403Secret key needed, website not allowed, or account disabled
429Hourly, daily or monthly limit reached; see Retry-After
5xxOur problem: send again with the same dedupeKey

One credit per recipient. Anything not sent (a duplicate, a blocked or unsubscribed address) is refunded at once. GET /v1/credits shows the balance and every change. Limits: 50 recipients per send, 1,000 messages per batch. Email pricing.

Full reference

Every endpoint (messages, templates, campaigns, unsubscribes, domains, webhooks, stats) with request and response examples and a console to try them:

Open the full Email API reference →

Also sending WhatsApp? See the WhatsApp API documentation: the same account, keys managed in the same panel.