Package Exports
- zeptomail
- zeptomail/package.json
Readme
ZeptoMail Node.js SDK
Send transactional email and manage your ZeptoMail account — agents, domains, suppression lists, and email logs from Node.js.
Table of Contents
- Installation
- Requirements
- Email Sending
- Authentication
- Agent Management
- Domain Management
- Suppression Management
- Email Log Retrieval
- Error Handling
- Contact Us
Installation
Sign up at ZeptoMail if you haven't already, then install the package:
npm install zeptomailRequirements
- Node.js ≥ 20
- Full TypeScript support is included — no separate
@types/package needed.
Note: Email sending uses a Send Mail Token for authentication and the management clients (Agents, Domains, Suppression, Email Logs) use OAuth 2.0 via AuthenticationClient.
Email Sending
Obtain your Send Mail Token from the ZeptoMail dashboard.
import { SendMailClient } from "zeptomail";
// CommonJS: const { SendMailClient } = require("zeptomail");
const client = new SendMailClient({
url: "api.zeptomail.com/", // required
token: "<Send mail token>", // required
});Send mail
await client.sendMail({
from: { address: "sender@example.com", name: "Sender" },
to: [
{ email_address: { address: "to@example.com", name: "Recipient" } }
],
reply_to: [{ address: "reply@example.com", name: "Reply" }],
cc: [{ email_address: { address: "cc@example.com", name: "CC" } }],
bcc: [{ email_address: { address: "bcc@example.com", name: "BCC" } }],
subject: "Sending with ZeptoMail",
htmlbody: "<strong>Easy to do from anywhere, with Node.js</strong>",
textbody: "Easy to do from anywhere, with Node.js",
track_clicks: true,
track_opens: true,
client_reference: "<client reference>",
mime_headers: { "X-Zylker-User": "test-xxxx" },
attachments: [
{ content: "<base64>", mime_type: "image/jpg", name: "report.jpg" },
{ file_cache_key: "<File Cache Key>", name: "cached-file" }
],
inline_images: [
{ content: "<base64>", mime_type: "image/jpg", cid: "img-header" },
{ file_cache_key: "<File Cache Key>", cid: "img-footer" }
]
});Send batch mail
Batch mail lets you send one request for up to 500 recipients, with per-recipient personalisation via merge_info. Use {{placeholder}} syntax in subject, htmlbody, textbody, or client_reference. The matching keys from each recipient's merge_info are substituted before delivery.
Two merge_info placement modes:
- Per-recipient only — set
merge_infoinside eachtoentry. Each recipient gets their own values. - Per-recipient + global fallback — set
merge_infoboth insidetoentries and at the top level. Recipients with their ownmerge_infouse it; recipients without one fall back to the top-level values.
await client.sendBatchMail({
from: { address: "sender@example.com", name: "Sender" },
to: [
{
email_address: { address: "alice@example.com", name: "Alice" },
merge_info: { name: "Alice", company: "Acme" } // per-recipient values
},
{
email_address: { address: "bob@example.com", name: "Bob" },
merge_info: { name: "Bob", company: "Globex" }
},
{
email_address: { address: "carol@example.com", name: "Carol" }
// no merge_info — falls back to top-level merge_info below
}
],
// Top-level merge_info: used as fallback for recipients without their own
merge_info: { name: "Customer", company: "Your Company" },
reply_to: [{ address: "reply@example.com", name: "Reply" }],
subject: "Hi {{name}}, a message from {{company}}",
htmlbody: "<p>Hi {{name}}, welcome to {{company}}.</p>",
textbody: "Hi {{name}}, welcome to {{company}}.",
track_clicks: true,
track_opens: true,
client_reference: "ref-{{name}}",
});Limit: A single batch request supports up to 500 recipients. To request a higher limit, contact support@zeptomail.com.
Send template mail
Uses a pre-built template from your ZeptoMail dashboard. Provide either template_key or template_alias (or both).
await client.sendMailWithTemplate({
template_key: "<template key>",
template_alias: "<template alias>",
from: { address: "sender@example.com", name: "Sender" },
to: [{ email_address: { address: "to@example.com", name: "Recipient" } }],
cc: [{ email_address: { address: "cc@example.com", name: "CC" } }],
bcc: [{ email_address: { address: "bcc@example.com", name: "BCC" } }],
reply_to: [{ address: "reply@example.com", name: "Reply" }],
merge_info: { contact_number: "8787xxxxxx789", company: "example.com" },
client_reference: "<client reference>",
mime_headers: { "X-Test": "test" }
});Send batch template mail
Combines a pre-built template with per-recipient merge_info for up to 500 recipients per request. The same two merge_info placement modes available in sendBatchMail apply here; per-recipient values inside to, with an optional top-level fallback for recipients that don't have their own.
await client.mailBatchWithTemplate({
template_key: "<template key>",
template_alias: "<template alias>",
from: { address: "sender@example.com", name: "Sender" },
to: [
{
email_address: { address: "alice@example.com", name: "Alice" },
merge_info: { contact: "9600000023", company: "Acme" } // per-recipient
},
{
email_address: { address: "bob@example.com", name: "Bob" },
// no merge_info — falls back to top-level merge_info below
}
],
// Top-level merge_info: fallback for recipients without their own
merge_info: { contact: "0000000000", company: "Default Co." },
reply_to: [{ address: "reply@example.com", name: "Reply" }],
client_reference: "<client reference>",
mime_headers: { "X-Test": "test" }
});Limit: A single batch request supports up to 500 recipients. To request a higher limit, contact support@zeptomail.com.
The clients for managing Agents, Domains, Suppression lists, and Email Logs use OAuth 2.0 rather than a send-mail token. Before using any of these clients, set up an
AuthenticationClientwith the appropriate scope.
Authentication — one client per scope
Each REST client requires its own AuthenticationClient instance. ZeptoMail issues scope-based refresh tokens — a token that authorises agent operations will not work for domain or suppression operations.
| Client | Specific scopes | All-access scope |
|---|---|---|
AgentClient |
Zeptomail.MailAgents.READZeptomail.MailAgents.CREATEZeptomail.MailAgents.UPDATEZeptomail.MailAgents.DELETE |
Zeptomail.MailAgents.All |
DomainClient |
Zeptomail.Domains.READZeptomail.Domains.CREATEZeptomail.Domains.UPDATEZeptomail.Domains.DELETE |
Zeptomail.Domains.All |
SuppressionClient |
Zeptomail.Suppressions.READZeptomail.Suppressions.CREATEZeptomail.Suppressions.UPDATEZeptomail.Suppressions.DELETE |
Zeptomail.Suppressions.All |
EmailLogsClient |
Zeptomail.email.READ |
— |
Use *.All to cover every operation under that client with a single token. Use specific scopes (e.g. Zeptomail.Domains.READ) to grant only the minimum privilege needed.
import { AuthenticationClient } from "zeptomail";
// One AuthenticationClient per scope — do NOT share across different clients
const agentAuth = new AuthenticationClient({
clientId: "<your-client-id>",
clientSecret: "<your-client-secret>",
refreshToken: "<agent-scoped-refresh-token>",
dataCenter: "US", // "US" | "EU" | "IN" | "AU" | "JP" | "CN"
// Optional:
timeout: 30_000, // token-refresh HTTP timeout in ms (default: 30 000)
bufferMs: 300_000, // refresh token this many ms before expiry (default: 5 min)
apiVersion: "v1.1", // API version (default: "v1.1")
});
// Separate instances per scope:
const domainAuth = new AuthenticationClient({ ... });
const suppressionAuth = new AuthenticationClient({ ... });
const emailLogsAuth = new AuthenticationClient({ ... });Each REST client accepts its AuthenticationClient via the constructor, or you can set it later:
const agents = new AgentClient({ authenticationClient: agentAuth });
// Or set after construction:
agents.setAuthenticationClient(agentAuth);Rotating a refresh token
Call updateCredentials() on the existing instance — no need to create a new client:
agentAuth.updateCredentials({ refreshToken: "<new-refresh-token>" });
// The cached access token is automatically invalidated; the next request fetches a fresh one.Pre-warming the token at startup
Call authenticate({ force: true }) to fetch a token before the first real request, avoiding latency on the first API call:
await agents.authenticate({ force: true });Disabling auto-refresh
By default, tokens are refreshed automatically. To manage tokens manually:
const agents = new AgentClient({
authenticationClient: agentAuth,
autoAuthenticate: false, // you are responsible for calling refreshAccessToken()
});
await agentAuth.refreshAccessToken(); // call this before making requests🧩 Agent Management
Manage mail agents (sending identities), their API keys, and SMTP passwords.
import { AgentClient } from "zeptomail";
const agents = new AgentClient({ authenticationClient: agentAuth });List all agents
Required scope:
Zeptomail.MailAgents.READorZeptomail.MailAgents.All
const { data } = await agents.getAllAgents();
// data: Agent[]Add an agent
Required scope:
Zeptomail.MailAgents.CREATEorZeptomail.MailAgents.All
const { data } = await agents.addAgent({
agentName: "My Agent",
description: "Production sending agent",
});
// data: AgentUpdate an agent
Required scope:
Zeptomail.MailAgents.UPDATEorZeptomail.MailAgents.All
const { data } = await agents.updateAgent({
agentKey: "<mailagent-key>",
agentName: "Updated Name",
description: "Updated description",
});Delete an agent
Required scope:
Zeptomail.MailAgents.DELETEorZeptomail.MailAgents.All
await agents.deleteAgent({ agentKey: "<mailagent-key>" });API Keys
// List API keys
// scope: Zeptomail.MailAgents.READ or .All
const { data } = await agents.getAPIKeys({ agentKey: "<mailagent-key>" });
// Generate a new API key
// scope: Zeptomail.MailAgents.CREATE or .All
const { data } = await agents.generateAPIKey({ agentKey: "<mailagent-key>" });
// data.password contains the plaintext key — store it securely
// Revoke an API key
// scope: Zeptomail.MailAgents.DELETE or .All
await agents.revokeAPIKey({ agentKey: "<mailagent-key>", id: "<api-key-id>" });SMTP Passwords
// List SMTP passwords
// scope: Zeptomail.MailAgents.READ or .All
const { data } = await agents.getSMTPList({ agentKey: "<mailagent-key>" });
// Generate a new SMTP password
// scope: Zeptomail.MailAgents.CREATE or .All
const { data } = await agents.generateSMTPPassword({ agentKey: "<mailagent-key>" });
// data.password contains the plaintext password — store it securely, it won't be shown again
// Delete an SMTP password
// scope: Zeptomail.MailAgents.DELETE or .All
await agents.deleteSMTPPassword({
agentKey: "<mailagent-key>",
passwordId: "<password-id>",
});🌐 Domain Management
Manage sending domains — add, verify, update, and delete.
import { DomainClient } from "zeptomail";
const domains = new DomainClient({ authenticationClient: domainAuth });List all domains
Required scope:
Zeptomail.Domains.READorZeptomail.Domains.All
const { data } = await domains.getAllDomains();
// data: DomainSummary[]Get domain details
Required scope:
Zeptomail.Domains.READorZeptomail.Domains.All
const { data } = await domains.getDomainDetails({ domainKey: "<domain-key>" });
// data: DomainDetail[] (includes DKIM and CNAME records)Add a domain
Required scope:
Zeptomail.Domains.CREATEorZeptomail.Domains.All
const { data } = await domains.addDomain({
domainName: "mail.example.com",
bounceDomainPrefix: "bounce", // forms bounce.mail.example.com
agentKeys: ["<mailagent-key>"], // optional
});
// data.dkim and data.cname contain the DNS records to configureUpdate a domain
Required scope:
Zeptomail.Domains.UPDATEorZeptomail.Domains.All
const { data } = await domains.updateDomain({
domainKey: "<domain-key>",
newDomainName: "mail2.example.com", // optional
newBounceDomainPrefix: "bounce2", // optional
associateAgents: ["<mailagent-key>"], // optional
disassociateAgents: ["<other-mailagent-key>"], // optional
});Verify a domain
Required scope:
Zeptomail.Domains.UPDATEorZeptomail.Domains.All
Triggers DNS verification. Configure the DKIM and CNAME records returned from addDomain / getDomainDetails in your DNS provider first.
const { data } = await domains.verifyDomain({ domainKey: "<domain-key>" });
// data.status will be "verified" if DNS records are correctDelete a domain
Required scope:
Zeptomail.Domains.DELETEorZeptomail.Domains.All
await domains.deleteDomain({ domainKey: "<domain-key>" });🚫 Suppression Management
Manage the Do-Not-Disturb (DND) / suppression list for email addresses and domains.
import { SuppressionClient } from "zeptomail";
const suppression = new SuppressionClient({ authenticationClient: suppressionAuth });List suppression entries
Required scope:
Zeptomail.Suppressions.READorZeptomail.Suppressions.All
const response = await suppression.getSuppressionList({
dndType: "email", // "email" | "domain"
// All filters below are optional:
dateFrom: new Date("2024-01-01"),
dateTo: "2024-12-31",
offset: 0,
limit: 50,
agentKey: "<mailagent-key>",
searchValue: "user@example.com",
});Add suppression entries
Required scope:
Zeptomail.Suppressions.CREATEorZeptomail.Suppressions.All
await suppression.addSuppressionEntry({
dndType: "email",
values: ["blocked@example.com", "spam@test.com"],
action: "reject", // "reject" | "suppress" | "suppress_tracking"
agentKeys: ["<mailagent-key>"], // optional — applies to all agents if omitted
description: "Bounced addresses", // optional
});Note:
"suppress_tracking"is only valid whendndTypeis"email".
Update suppression entries
Required scope:
Zeptomail.Suppressions.UPDATEorZeptomail.Suppressions.All
await suppression.updateSuppressionEntry({
dndType: "email",
values: ["blocked@example.com"],
action: "suppress",
});Delete suppression entries
Required scope:
Zeptomail.Suppressions.DELETEorZeptomail.Suppressions.All
await suppression.deleteSuppressionEntry({
dndType: "domain",
values: ["spam.com", "malicious.org"],
});📋 Email Logs
Query sent email logs and retrieve full details for a specific email.
import { EmailLogsClient } from "zeptomail";
const logs = new EmailLogsClient({ authenticationClient: emailLogsAuth });List email logs
Required scope:
Zeptomail.email.READ
All parameters are optional.
const response = await logs.getAllEmailLogs({
mailAgentKey: "<mailagent-key>",
requestId: "<request-id>",
dateFrom: new Date("2024-06-01"),
dateTo: new Date("2024-06-30"),
from: "sender@example.com",
to: "recipient@example.com",
cc: "cc@example.com",
bcc: "bcc@example.com",
recipient: "any@example.com", // matches to / cc / bcc
subject: "Welcome",
clientReference: "<client-ref>",
offset: 0,
limit: 100,
isHardBounced: false,
isSoftBounced: false,
isMailFailure: false,
isDelivered: true,
});Get full details for a single email
Required scope:
Zeptomail.email.READ
const response = await logs.getEmailDetails({
emailReference: "<email-reference-id>",
});⚠️ Error Handling
All clients throw typed errors. Import the classes you need and use instanceof to branch:
import { AuthenticationError, NetworkError, TimeoutError, ApiError, ValidationError } from "zeptomail";
try {
const { data } = await agents.getAllAgents();
} catch (err) {
if (err instanceof ValidationError) {
// A required parameter was missing or invalid before the request was sent.
} else if (err instanceof AuthenticationError) {
// Token refresh failed — check clientId, clientSecret, refreshToken.
} else if (err instanceof NetworkError) {
// Could not reach the server — check connectivity.
} else if (err instanceof TimeoutError) {
// The token-refresh or API request timed out.
} else if (err instanceof ApiError) {
// The server returned an HTTP error response.
}
}📒 Contact Us
Contact us at support@zeptomail.com for any issue resolutions or assistance.