npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

zeptomail

v8.0.1

Published

Send mail via Node.js with ZeptoMail

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

Sign up at ZeptoMail if you haven't already, then install the package:

npm install zeptomail

Requirements

  • 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: "[email protected]", name: "Sender" },
    to: [
        { email_address: { address: "[email protected]", name: "Recipient" } }
    ],
    reply_to: [{ address: "[email protected]", name: "Reply" }],
    cc: [{ email_address: { address: "[email protected]", name: "CC" } }],
    bcc: [{ email_address: { address: "[email protected]", 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_info inside each to entry. Each recipient gets their own values.
  • Per-recipient + global fallback — set merge_info both inside to entries and at the top level. Recipients with their own merge_info use it; recipients without one fall back to the top-level values.
await client.sendBatchMail({
    from: { address: "[email protected]", name: "Sender" },
    to: [
        {
            email_address: { address: "[email protected]", name: "Alice" },
            merge_info: { name: "Alice", company: "Acme" }  // per-recipient values
        },
        {
            email_address: { address: "[email protected]", name: "Bob" },
            merge_info: { name: "Bob", company: "Globex" }
        },
        {
            email_address: { address: "[email protected]", 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: "[email protected]", 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 [email protected].

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: "[email protected]", name: "Sender" },
    to: [{ email_address: { address: "[email protected]", name: "Recipient" } }],
    cc: [{ email_address: { address: "[email protected]", name: "CC" } }],
    bcc: [{ email_address: { address: "[email protected]", name: "BCC" } }],
    reply_to: [{ address: "[email protected]", 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: "[email protected]", name: "Sender" },
    to: [
        {
            email_address: { address: "[email protected]", name: "Alice" },
            merge_info: { contact: "9600000023", company: "Acme" } // per-recipient
        },
        {
            email_address: { address: "[email protected]", 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: "[email protected]", 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 [email protected].



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 AuthenticationClient with 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.READ or Zeptomail.MailAgents.All

const { data } = await agents.getAllAgents();
// data: Agent[]

Add an agent

Required scope: Zeptomail.MailAgents.CREATE or Zeptomail.MailAgents.All

const { data } = await agents.addAgent({
    agentName: "My Agent",
    description: "Production sending agent",
});
// data: Agent

Update an agent

Required scope: Zeptomail.MailAgents.UPDATE or Zeptomail.MailAgents.All

const { data } = await agents.updateAgent({
    agentKey: "<mailagent-key>",
    agentName: "Updated Name",
    description: "Updated description",
});

Delete an agent

Required scope: Zeptomail.MailAgents.DELETE or Zeptomail.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.READ or Zeptomail.Domains.All

const { data } = await domains.getAllDomains();
// data: DomainSummary[]

Get domain details

Required scope: Zeptomail.Domains.READ or Zeptomail.Domains.All

const { data } = await domains.getDomainDetails({ domainKey: "<domain-key>" });
// data: DomainDetail[]  (includes DKIM and CNAME records)

Add a domain

Required scope: Zeptomail.Domains.CREATE or Zeptomail.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 configure

Update a domain

Required scope: Zeptomail.Domains.UPDATE or Zeptomail.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.UPDATE or Zeptomail.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 correct

Delete a domain

Required scope: Zeptomail.Domains.DELETE or Zeptomail.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.READ or Zeptomail.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: "[email protected]",
});

Add suppression entries

Required scope: Zeptomail.Suppressions.CREATE or Zeptomail.Suppressions.All

await suppression.addSuppressionEntry({
    dndType: "email",
    values: ["[email protected]", "[email protected]"],
    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 when dndType is "email".

Update suppression entries

Required scope: Zeptomail.Suppressions.UPDATE or Zeptomail.Suppressions.All

await suppression.updateSuppressionEntry({
    dndType: "email",
    values: ["[email protected]"],
    action: "suppress",
});

Delete suppression entries

Required scope: Zeptomail.Suppressions.DELETE or Zeptomail.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: "[email protected]",
    to: "[email protected]",
    cc: "[email protected]",
    bcc: "[email protected]",
    recipient: "[email protected]", // 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 [email protected] for any issue resolutions or assistance.