Skip to content
MailMarcodocs

12 · MailMarco API

SDK & CLI

A thin typed wrapper over the same HTTP routes. Everything it does, you can do with curl — it just saves you the header plumbing.


Install

shell
npm install @mailmarco/sdk-node

The package is ESM-only and targets Node 18 or newer, because it uses the global fetch. There are no runtime dependencies.

Creating a client

typescript
import { MailMarco } from "@mailmarco/sdk-node";

const mm = new MailMarco({
  apiKey: process.env.MAILMARCO_API_KEY!,
  baseUrl: "https://api.mailmarco.com",
});

Every call sends authorization: Bearer <apiKey> and content-type: application/json. The SDK covers the API-key surface only — domain management through the SDK will fail with a 401, because those routes want a session token.

Emails

typescript
// Send. The second argument sets the Idempotency-Key header.
const sent = await mm.emails.send(
  {
    from: "[email protected]",
    to: ["[email protected]"],
    subject: "Your receipt",
    html: "<p>Thanks for your order.</p>",
    text: "Thanks for your order.",
    tags: { kind: "receipt" },
  },
  { idempotencyKey: "order-2291-receipt" },
);

console.log(sent.id, sent.status); // "b1d2…", "queued"

// Retrieve, including the event timeline.
const message = await mm.emails.get(sent.id);

// Cancel a scheduled message.
await mm.emails.cancel(sent.id);

Domains

typescript
const domains = await mm.domains.list();
const created = await mm.domains.create("mail.example.com");
const records = await mm.domains.dnsRecords(created.id);
await mm.domains.verify(created.id);

These call the same routes as the Domains page, which are session-authenticated — so they only work if you construct the client with a session access token in apiKey. That works because the SDK just puts the value in the bearer header.

Suppressions

typescript
await mm.suppressions.list();
await mm.suppressions.add("[email protected]");            // scope defaults to "tenant"
await mm.suppressions.add("example.net", "domain");           // block a whole domain
await mm.suppressions.remove(suppressionId);

Error handling

Any non-2xx throws a MailMarcoError carrying the HTTP status and the API's error field, so you can branch on the same codes documented in Errors & limits.

typescript
import { MailMarco, MailMarcoError } from "@mailmarco/sdk-node";

try {
  await mm.emails.send(input, { idempotencyKey: key });
} catch (err) {
  if (!(err instanceof MailMarcoError)) throw err;

  switch (err.status) {
    case 402:
      // err.code === "plan_limit_exceeded"
      await alertBilling(err.message);
      break;
    case 403:
      // err.code === "org_suspended"
      await pauseOutbox();
      break;
    case 429:
      // err.code === "warmup_cap_reached" — retry tomorrow, not in a few seconds
      await deferUntilTomorrow();
      break;
    case 400:
      // validation or a domain rule — never retry unchanged
      logPermanentFailure(err.message);
      break;
    default:
      throw err;
  }
}
PropertyValue
statusThe HTTP status code.
codeThe response's error field, or "error" when absent.
messageThe response's message, falling back to the code and then to HTTP <status>.

OpenAPI spec

A machine-readable OpenAPI 3.1.0 description of the API is served from this site:

shell
curl https://docs.mailmarco.com/openapi.json -o mailmarco.openapi.json

# generate a client, or import it into an HTTP tool
npx @openapitools/openapi-generator-cli generate \
  -i mailmarco.openapi.json -g typescript-fetch -o ./generated

The document is authored against the real controllers and schemas — including the success statuses that surprise people, such as 201 on /cancel and /verify. It declares two bearer security schemes, apiKey and session, so each operation states which credential it wants.

CoveredNot yet covered
emails, domains, templates, suppressions, bounces, complaints, webhooks, api-keysmarketing, inbound, reputation, billing, admin

The uncovered areas are fully documented in prose on this site — see Marketing and Inbound — they are simply not in the spec document yet.