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.
https://msg.sisplerp.com/mailAuthorization: Bearer sk_live_…Get your keys in the panel: Email → Keys & API. Create a free account to start with free emails.
Keys
| Key | Use it for | Never |
|---|---|---|
| sk_live_… | Everything, from your server | Put 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 outcomes | Expect real delivery |
Quick start
Send a one-time password with the ready-made otp template:
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"}}'$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']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
| Field | What it is |
|---|---|
| to | Required. One address or a list (up to 50 recipients in total with cc and bcc) |
| template + vars | A ready-made template or one of yours, with its values. Or use subject with html / text |
| subject, html, text | Your own email. text is made from html when you leave it out |
| cc, bcc | Each address is charged a credit |
| attachments | Up to 5 files, 5 MB in total: [{ filename, contentType, content (base64) }] |
| fromAddress | Another address on your verified domain, e.g. accounts@yourschool.in |
| fromName, replyTo | Sender name and reply address for this email only |
| dedupeKey | Send the same key again and the email is not sent twice; comes back in webhooks as reference |
| sendAt | Send later, e.g. 2026-10-08T09:00:00+05:30 |
| priority | high 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.
| Template | Values |
|---|---|
| otp | code, appLabel, purpose, expiryMinutes |
| password-reset | code or resetUrl, appLabel, expiryMinutes |
| verify-email | verifyUrl, name, code, appLabel |
| welcome | name, appLabel, username, loginUrl |
| fee-receipt | receiptNo, studentName, amount, paidOn, paymentMode |
| receipt | receiptNo, amount, paidOn, customerName |
| reminder | title, message, name, dueDate, amount |
| notice | subject, heading, intro, bodyHtml, actionUrl |
Common tasks
Fee receipt with the PDF attached
{ "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
{ "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
<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.
| Setting | Value |
|---|---|
| Server | smtp.sisplerp.com |
| Port | 587 with STARTTLS (called "TLS" in most apps), or 465 with SSL/TLS |
| Username | Your account name, shown on Email → Keys & API |
| Password | A 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 -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 onceEvents: 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")>.
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
| Status | Means |
|---|---|
| 400 | The request is wrong; error, message and field say how |
| 401 | Missing, unknown or revoked key |
| 402 | Not enough credits: top up in the panel, then send again |
| 403 | Secret key needed, website not allowed, or account disabled |
| 429 | Hourly, daily or monthly limit reached; see Retry-After |
| 5xx | Our 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.