Skip to content

Repository files navigation

@mailneo/sdk

TypeScript SDK for the Mailneo public API (v2).

  • Zero runtime dependencies — built on fetch. Node 18+ and modern browsers.
  • Types generated from the OpenAPI spec (openapi/openapi.json), so the compiler knows exactly what every endpoint accepts and returns.
  • ESM and CommonJS builds.

Install

npm install @mailneo/sdk

Quickstart

import { MailneoClient } from "@mailneo/sdk";

const client = new MailneoClient({ apiKey: process.env.MAILNEO_API_KEY! });
// or, with an OAuth 2.1 access token:
// const client = new MailneoClient({ accessToken });

// Who am I?
const me = await client.me();
console.log(me.data.scopes);

// Automatic cursor pagination — pages are fetched lazily as you iterate.
for await (const subscriber of client.subscribers.list({ status: "ENABLED" })) {
  console.log(subscriber.email);
}

// Create, with an Idempotency-Key so the write is safely retryable.
const created = await client.subscribers.create(
  { email: "ada@example.com", first_name: "Ada" },
  { idempotencyKey: "create-ada-2026-08-04" }
);

Surface

One namespace per API tag: subscribers, lists, segments, contacts, campaigns, newsletters, templates, suppressions, analytics, accounts, webhooks, system (plus client.me() / client.usage() shortcuts). The namespaces are generated from the OpenAPI spec by npm run generate; only the thin core (auth, retries, pagination, errors) is hand-written.

Methods return the API's envelope as-is: { data, meta }. meta.request_id is what Mailneo support asks for; on a dry_run call, data is null and meta.impact describes what would have happened.

Pagination

List methods return a lazy CursorPage:

const page = client.subscribers.list({ limit: 100 });

for await (const item of page) { /* items across every page */ }
for await (const envelope of page.pages()) { /* raw pages */ }
await page.firstPage();  // exactly one request
await page.toArray(500); // bounded drain

For incremental sync, read meta.synced_through from the final page (has_more: false) and pass it back as updated_since on the next run. The server deliberately omits it mid-walk; the pages() iterator is how you reach it. Expect to occasionally re-see a row — upsert by id.

Retries

  • Retried: HTTP 429, 500, 502, 503, 504, and connection failures.
  • A request is only retried when repeating it is safe: idempotent methods (GET/PUT/DELETE), or a write carrying an Idempotency-Key. A POST/PATCH without a key is never retried — pass { idempotencyKey } to opt in.
  • Retry-After is respected exactly (seconds or HTTP-date). If it exceeds maxRetryAfterMs (default 60 s) the SDK throws instead of stalling.
  • Otherwise: full-jitter exponential backoff (retryBaseMs 500 ms, doubling, capped at retryCapMs 30 s). Default maxRetries is 2.

Errors

Non-2xx responses throw:

  • MailneoApiError — the API's error envelope, with status, type, code (stable, branch on it), message, param, docUrl, requestId, retryAfterMs.
  • MailneoHttpError — a non-2xx whose body was not the envelope (proxy pages, HTML). No fabricated code.
  • MailneoConnectionErrorfetch itself failed.

Options

new MailneoClient({
  apiKey,            // or accessToken — one is required
  baseUrl,           // default https://api.mailneo.co/api/v2
  fetch,             // injectable (tests, polyfills)
  maxRetries,        // default 2
  retryBaseMs,       // default 500
  retryCapMs,        // default 30_000
  maxRetryAfterMs,   // default 60_000
  defaultHeaders,    // merged under auth headers
});

Per-write options: { idempotencyKey, dryRun, signal, headers }. Operations the spec marks x-mailneo-idempotency: required (e.g. contacts.bulkUpsert, webhooks.replayDelivery) require idempotencyKey at the type level.

Development

npm ci
npm run generate   # regen src/generated/* from openapi/openapi.json
npm run typecheck  # includes examples/usage.ts, the compile-time smoke test
npm test           # vitest, mocked fetch, no network
npm run build      # tsup: ESM + CJS + d.ts

openapi/openapi.json is a vendored snapshot of the spec the Mailneo API publishes, and src/generated/ is committed so the package builds standalone; refresh the snapshot and regenerate whenever the API spec changes.

About

Official TypeScript SDK for the Mailneo API

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages