Keep your SMTP
Gmail, SendGrid SMTP, Amazon SES, or custom host — connect once under Dashboard → Service. Zindua is not your ESP.

Nodemailer is a great SMTP client. Zindua is not a replacement — it is the layer that hosts templates, traces delivery, and adds WhatsApp on the same API. Keep your mail provider. Drop the transporter boilerplate.
npm install @zindua/sdk@1.4.0Guide v1.0.0 · @zindua/sdk v1.4.0
Gmail, SendGrid SMTP, Amazon SES, or custom host — connect once under Dashboard → Service. Zindua is not your ESP.

No createTransport(), no HTML strings in handlers. One zindua.send({ template, variables }) after you push the template.

OTP on email today, WhatsApp tomorrow — same template slug, channel: "whatsapp". Nodemailer cannot do that.

French, English, Swahili versions live in the dashboard. lang + {{var|fallback}} — not duplicated sendMail blocks.

Redis, Postgres, your ORM. Zindua delivers the message and returns logId. Your database stays yours.
logId, Dashboard → Logs, webhooks email.delivered / email.failed. No custom logging table required.

Same OTP function. Toggle Nodemailer today vs coupled with Zindua.
Nodemailer today
Transporter, credentials, and inline HTML in your codebase.
mailer.ts
1import nodemailer from "nodemailer";2 3const transporter = nodemailer.createTransport({4 host: process.env.SMTP_HOST,5 port: 465,6 secure: true,7 auth: {8 user: process.env.SMTP_USER,9 pass: process.env.SMTP_PASS,10 },11});12 13export async function sendOtpEmail(to: string, code: string) {14 await transporter.sendMail({15 from: process.env.MAIL_FROM,16 to,17 subject: "Your verification code",18 html: `<p>Hi,</p><p>Your code is <strong>${code}</strong>. Expires in 10 minutes.</p>`,19 });20 // No logId — you add logging yourself21}Seven decisions every team makes around sendMail — and how coupling simplifies each one.
Nodemailer is a mailer library. Zindua is delivery orchestration on top of your provider.
Who sends the email?
Your Node process calls SMTP directly via Nodemailer.
Zindua dispatches through the provider you connected (still your SMTP credentials).
Zindua orchestrates delivery. It does not become your database or your ESP.
Coupling with Zindua does not move your data or reputation.
Your SMTP provider and sending reputation
The same host and credentials you use with Nodemailer — connected once under Dashboard → Service.
Connect Service → template → SDK → swap sendMail.
Four steps. Your SMTP host does not change — only where credentials live.
Connect Service
Dashboard → Project → Service. Add the same SMTP host, port, and auth you already use with Nodemailer.
Copy-paste reference for a typical OTP mailer module.
Nodemailer today
import nodemailer from "nodemailer"; const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: 465, secure: true, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, },}); export async function sendOtpEmail(to: string, code: string) { await transporter.sendMail({ from: process.env.MAIL_FROM, to, subject: "Your verification code", html: `<p>Hi,</p><p>Your code is <strong>${code}</strong>. Expires in 10 minutes.</p>`, }); // No logId — you add logging yourself}With Zindua
import { Zindua } from "@zindua/sdk"; const zindua = new Zindua({ apiKey: process.env.ZINDUA_API_KEY! }); export async function sendOtpEmail(to: string, code: string) { const result = await zindua.send({ to, template: "otp-verification", lang: "fr", variables: { code, appName: "MyApp" }, }); return result.logId; // Dashboard → Logs, webhooks}// Optional: keep Nodemailer for edge cases, Zindua for product email. // Most teams delete the transporter once templates are on Zindua.
doctor validates your API key. send smoke-tests a template against your connected Service.
npx @zindua/cli@latest doctorConnect the same SMTP you use with Nodemailer, push one template, replace sendMail with zindua.send.