Skip to content
Last updated

Node.js SDK

The official JavaScript SDK is published on npm as node-mailjet and developed at mailjet/mailjet-apiv3-nodejs. It is written in TypeScript and ships full type coverage.

The SDK runs in Node.js and in the browser. In the browser a proxy is currently required because of CORS restrictions — and your private API key must never be shipped in frontend code.

Compatibility

Officially supported Node.js versions: >= 12.x.

Upgrading to v7

Version 7.0.0 contains breaking changes. In short:

  • Entry points are restricted. package.json now declares an exports map, so only require('node-mailjet') and import ... from 'node-mailjet' resolve. Deep imports such as require('node-mailjet/dist/mailjet.node.js') no longer work.
  • .id() must come before .action(). Calling .id() after .action() now throws instead of silently building an incorrect URL.
  • contactdata PUT is validated locally. A PUT to contactdata throws synchronously if Data is missing, is not an array, or contains entries without Name / Value keys — instead of failing later with a server-side 400.
  • Requests time out after 30s by default instead of hanging indefinitely. Pass options.timeout to change it.

The full list is in the CHANGELOG.

Installation

npm install node-mailjet

Set up the client

Authentication

The Email API and Send API authenticate with your API key and secret. The SMS API uses a bearer token generated in the SMS section of your account.

export MJ_APIKEY_PUBLIC='your API key'
export MJ_APIKEY_PRIVATE='your API secret'
export MJ_API_TOKEN='your API token'

CommonJS and native ESM (including named imports) are both supported:

const Mailjet = require('node-mailjet');
import Mailjet, { Client } from 'node-mailjet';

Email API and Send API

const mailjet = new Mailjet({
  apiKey: process.env.MJ_APIKEY_PUBLIC,
  apiSecret: process.env.MJ_APIKEY_PRIVATE
});

Or with the apiConnect helper:

const mailjet = Mailjet.apiConnect(
  process.env.MJ_APIKEY_PUBLIC,
  process.env.MJ_APIKEY_PRIVATE,
  {
    config: {},
    options: {}
  }
);

SMS API

const mailjet = Mailjet.smsConnect(process.env.MJ_API_TOKEN);

Send your first email

const Mailjet = require('node-mailjet');

const mailjet = Mailjet.apiConnect(
  process.env.MJ_APIKEY_PUBLIC,
  process.env.MJ_APIKEY_PRIVATE,
);

mailjet
  .post('send', { version: 'v3.1' })
  .request({
    Messages: [
      {
        From: {
          Email: 'pilot@example.com',
          Name: 'Mailjet Pilot'
        },
        To: [
          {
            Email: 'passenger1@example.com',
            Name: 'Passenger 1'
          }
        ],
        Subject: 'Your email flight plan!',
        TextPart: 'Dear passenger 1, welcome to Mailjet!',
        HTMLPart: '<h3>Dear passenger 1, welcome to Mailjet!</h3>'
      }
    ]
  })
  .then((result) => console.log(result.body))
  .catch((err) => console.log(err.statusCode));

Configuration

Requests are composed from a method, a resource, a config object and the request data:

const mailjet = new Mailjet({
  apiKey: process.env.MJ_APIKEY_PUBLIC,
  apiSecret: process.env.MJ_APIKEY_PRIVATE,
  config: CONFIG,
  options: OPTIONS
});

const request = mailjet
  .METHOD(RESOURCE, CONFIG)
  .request(DATA, PARAMS, PERFORM_API_CALL);
ArgumentMeaning
METHODpost, put, get or delete
RESOURCEthe API endpoint to call
CONFIGconnection config — see Config
OPTIONSconnection options — see Options
DATArequest body (post, put, delete)
PARAMSURL query parameters
PERFORM_API_CALLboolean; set false to build the request without sending it

Config and options can be set once on the client and reused for every request, or passed per request — a per-request config takes precedence.

Options

OptionEffect
headersadditional request headers
timeoutmilliseconds before the request is aborted (default 30000 since v7)
proxyproxy host, port, protocol and auth (Node only)
maxBodyLengthmaximum request content size in bytes (Node only)
maxContentLengthmaximum response content size in bytes (Node only)
const mailjet = new Mailjet({
  apiKey: process.env.MJ_APIKEY_PUBLIC,
  apiSecret: process.env.MJ_APIKEY_PRIVATE,
  options: {
    timeout: 1000,
    maxBodyLength: 1500,
    maxContentLength: 100,
    headers: {
      'X-API-Key': 'foobar',
    },
    proxy: {
      protocol: 'http',
      host: 'www.test-proxy.com',
      port: 3100,
    }
  }
});

Options are passed through to the underlying HTTP client — see the axios request config for details.

Config

KeyEffect
hostbase host name (default api.mailjet.com)
versionAPI version — v3, v3.1 or v4
outputresponse data type

API versioning

VersionScope
v3Email API
v3.1Send API v3.1 (latest send version)
v4SMS API, contact delete API

Most Email API endpoints sit under v3, which is the default and does not need to be specified. For anything else, set version:

const request = mailjet.post('send', { version: 'v3.1' });

Host URL

const request = mailjet.post('send', { version: 'v3.1', host: 'api.us.mailjet.com' });

Accounts on Mailjet's US architecture must set api.us.mailjet.com.

Response output

The default output is json. Supported values: arraybuffer, document, json, text, stream, and blob (browser only).

const request = mailjet.post('send', { version: 'v3.1', output: 'arraybuffer' });

Disable the API call

Useful in tests, to build and inspect a request without reaching Mailjet:

const request = mailjet
  .post('send', { version: 'v3.1' })
  .request({}, {}, false);

TypeScript

All Mailjet types are exported from the package entry point:

import {
  Contact,
  SendEmailV3,
  SendEmailV3_1,
  Message,
  Segmentation,
  Template,
  SendMessage,
  Webhook
} from 'node-mailjet';

Request.request<TResult>(data, params, performAPICall) is generic, so responses are typed:

import { Client, SendEmailV3_1, LibraryResponse } from 'node-mailjet';

const mailjet = new Client({
  apiKey: process.env.MJ_APIKEY_PUBLIC,
  apiSecret: process.env.MJ_APIKEY_PRIVATE
});

const data: SendEmailV3_1.Body = {
  Messages: [
    {
      From: { Email: 'pilot@example.com' },
      To: [{ Email: 'passenger@example.com' }],
      Subject: 'Your email flight plan!',
      HTMLPart: '<h3>Dear passenger, welcome to Mailjet!</h3>',
      TextPart: 'Dear passenger, welcome to Mailjet!',
    },
  ],
};

const result: LibraryResponse<SendEmailV3_1.Response> = await mailjet
  .post('send', { version: 'v3.1' })
  .request(data);

const { Status } = result.body.Messages[0];

For library versions 3.*.* and below, use the community-maintained @types/node-mailjet package instead.

Batch sending and partial failures

Send API v3.1 accepts multiple emails in one request through the Messages array. This is not an atomic operation. Each entry is validated and processed independently, so a single request can return a mix of successes and failures. A 2xx response only means Mailjet accepted the request for processing — it does not mean every message was sent, and one failing message does not stop the others.

Always inspect the Status of each entry in result.body.Messages:

  • success — the message was accepted and is being delivered.
  • error — the message failed; details are in that entry's Errors array (ErrorCode, ErrorMessage, ErrorIdentifier).

Set AdvanceErrorHandling: true to get one Errors entry per validation problem, with machine-readable codes.

const data: SendEmailV3_1.Body = {
  Messages: [
    {
      From: { Email: 'pilot@example.com' },
      To: [{ Email: 'passenger1@example.com' }],
      Subject: 'Your flight plan (1)',
      TextPart: 'This message is expected to succeed.',
      CustomID: 'order-1001',
    },
    {
      From: { Email: 'pilot@example.com' },
      To: [{ Email: 'not-a-valid-address' }],
      Subject: 'Your flight plan (2)',
      TextPart: 'This message is expected to fail.',
      CustomID: 'order-1002',
    },
  ],
  AdvanceErrorHandling: true,
};

const result: LibraryResponse<SendEmailV3_1.Response> = await mailjet
  .post('send', { version: 'v3.1' })
  .request(data);

const succeeded = result.body.Messages.filter((message) => message.Status === 'success');
const failed = result.body.Messages.filter((message) => message.Status !== 'success');

Note that failed being non-empty does not mean the promise rejected — the request itself succeeded.

Retrying without duplicating sends

Blindly retrying a whole batch resends messages that already succeeded. To retry safely:

  1. Give every message a unique CustomID before you send it, and record it against your own order or notification ID.
  2. Collect only the messages whose Status is error — never re-include successes.
  3. Use the failed entry's Errors[].StatusCode / ErrorCode to decide whether a retry makes sense. Validation errors (invalid recipient, malformed address, blocked domain) will fail again unless the message itself changes; transient errors (StatusCode >= 500) may succeed.
  4. Resubmit only the failed messages, reusing the same CustomID, so retries stay correlated in your logs and in Mailjet's.
  5. Treat a request-level rejection (network failure, auth failure, malformed payload) differently: Mailjet never processed the batch, so the entire Messages array is safe to retry as-is.

A runnable example lives in examples/node/src/batchSend.js.

Request examples

POST — create an object

mailjet
  .post('contact')
  .request({
    Email: 'passenger@example.com',
    IsExcludedFromCampaigns: true,
    Name: 'New Contact'
  })
  .then((result) => console.log(result.body))
  .catch((err) => console.log(err.statusCode));

POST — endpoints with an action

Call .id() before .action():

mailjet
  .post('contact')
  .id(contactID)
  .action('managecontactslists')
  .request({
    ContactsLists: [
      {
        ListID: listID,
        Action: 'addnoforce'
      }
    ]
  })
  .then((result) => console.log(result.body))
  .catch((err) => console.log(err.statusCode));

GET — all objects

mailjet.get('contact').request();

GET — with filters

Query parameters go in the second .request() argument:

mailjet.get('contact').request({}, { IsExcludedFromCampaigns: false });

GET — a single object

mailjet.get('contact').id(contactID).request();

For the contact and contactdata resources, .id() also accepts a contact's email address instead of the numeric ID (.id('user@example.com')). Pass it as-is — the SDK does not URL-encode it, which matches what the REST API expects.

PUT — update an object

A PUT in the Mailjet API behaves like a PATCH: only the properties you send are updated, and non-mandatory properties can be omitted.

Each entry in Data must be an object with Name and Value keys, where Name matches a property already defined via /contactmetadata. Sending the property directly ({ first_name: 'John' }) instead of { Name: 'first_name', Value: 'John' } returns an Invalid key name error.

mailjet
  .put('contactdata')
  .id(contactID)
  .request({
    Data: [
      { Name: 'first_name', Value: 'John' },
      { Name: 'last_name', Value: 'Smith' }
    ]
  });

Updating template content

PUT / POST on the template resource only updates metadata (Name, Categories, Purposes, …). It does not touch the template body, so changes made that way appear in a GET template response but never in the Mailjet App editor. To update the actual content, target the template/{id}/detailcontent sub-resource:

mailjet
  .put('template')
  .id(templateID)
  .action('detailcontent')
  .request({
    'Html-part': '<html><body><p>Hello {{var:name}}</p></body></html>',
    'Text-part': 'Hello {{var:name}}'
  });

DELETE — remove an object

A successful DELETE returns 204 No Content with no response body.

mailjet.delete('template').id(templateID).request();

A 204 here — and a subsequent 404 on GET template/{id} — confirms the template was removed API-side.

Deleting a contact (GDPR)

Contact deletion uses a dedicated v4 endpoint, so the resource name is the plural contacts and the version must be set explicitly:

await mailjet
  .delete('contacts', { version: 'v4' })
  .id(contactID)
  .request();

See Delete a contact for details.

SMS API

const Mailjet = require('node-mailjet');

const mailjet = Mailjet.smsConnect(process.env.MJ_API_TOKEN, {
  config: { version: 'v4' }
});

mailjet
  .post('sms-send')
  .request({
    Text: 'Have a nice SMS flight with Mailjet!',
    To: '+33600000000',
    From: 'MJPilot'
  })
  .then((result) => console.log(result.body))
  .catch((err) => console.log(err.statusCode));

Example applications

The repository includes runnable examples for several environments:

  • Browser — RequireJS-based request playground
  • Node — simple request scripts
  • Sendmail — Express app that lists contacts and sends email
  • React — React request playground
  • Firebase — Firebase Functions for static and query-driven sends

Browser-side examples still require a proxy because of CORS.

Contribute

The SDK is open source. Fork the repository, branch, implement your fix or feature, document it, and open a pull request at mailjet/mailjet-apiv3-nodejs. Documentation improvements belong in the API documentation repo.