TypeScript SDK

@atrix-mail/sdk is a small typed client over the sending API — one class, fully-typed requests and responses, a typed error, and webhook verification built in.

The SDK is ESM, strict-TypeScript, and dependency-light — it uses the runtime's fetch and node:crypto, and re-uses the platform's own request/response types so it can never drift from the API.

Install

shell
bun add @atrix-mail/sdk
# or: npm i @atrix-mail/sdk

Create a client

ts
import { AtrixMail } from "@atrix-mail/sdk";

const mail = new AtrixMail(process.env.ATRIX_MAIL_KEY!); // am_live_...

// options (all optional):
new AtrixMail(apiKey, {
  baseUrl: "https://api.mail.atrix.dev", // default
  timeoutMs: 30_000,                     // default
  fetch: customFetch,                    // defaults to globalThis.fetch
});

Send email

ts
const { id, status } = await mail.emails.send({
  from: "Yourco <hello@yourco.com>",
  to: ["ava@client.io"],
  subject: "Your March invoice",
  html: "<p>Invoice attached.</p>",
  text: "Invoice attached.",
  reply_to: "support@yourco.com",
  attachments: [
    { filename: "invoice.pdf", content: pdfBase64, contentType: "application/pdf" },
  ],
  idempotency_key: "invoice-1042", // safe retries
});
// id: "<uuid>", status: "queued"

send takes the full SendEmailInput contract and returns { id, status }.

Retrieve a sent email

ts
const email = await mail.emails.get(id); // needs an emails.read-scoped key
console.log(email.status, email.events);

Beyond sending

The client covers the whole key-authenticated surface, not just transactional mail — each resource needs its own scope on the key.

ts
// Domains — add one, read back what DNS is still wrong, re-check.
const { dns_records } = await mail.domains.create("acme.com");
const { grade } = await mail.domains.get(domainId);
await mail.domains.verify(domainId);

// Audiences — lists and their contacts.
const { audience } = await mail.audiences.create("Changelog");
await mail.audiences.importContacts(audience.id, [
  { email: "ava@client.io", first_name: "Ava" },
]);

// Broadcasts — draft, then send or schedule.
const { broadcast } = await mail.broadcasts.create({
  audience_id: audience.id,
  name: "March changelog",
  from: "Yourco <hello@acme.com>",
  subject: "What shipped in March",
  html: "<p>…</p>",
});
await mail.broadcasts.send(broadcast.id);

// Mailboxes — hosting. The password is set once, here.
const { mailbox } = await mail.mailboxes.create({
  domain_id: domainId,
  local_part: "support",
  password: generatedPassword,
});
const { connection } = await mail.mailboxes.connectionInfo(mailbox.id);

// Webhooks — the secret comes back once, on create.
const { webhook } = await mail.webhookEndpoints.create("https://acme.com/hook", [
  "email.delivered",
  "email.bounced",
]);

// Templates, and analytics.
await mail.templates.create({ name: "welcome", subject: "Welcome, {{name}}", html: "<p>Hi {{name}}</p>" });
const stats = await mail.analytics.deliverability();  // 30-day rates + series
const { entries } = await mail.analytics.activity({ status: "bounced" });

Public API

MemberSignature
new AtrixMail(apiKey: string, options?: AtrixMailOptions)
emails.send(input: SendEmailInput) => Promise<SendEmailResponse>
emails.get(id: string) => Promise<RetrievedEmail>
domainscreate · list · get · update · verify · delete
mailboxescreate · list · get · update · delete · connectionInfo · createAlias · listAliases · deleteAlias
audiencescreate · list · get · delete · addContact · importContacts · listContacts · deleteContact
broadcastscreate · list · get · send · delete
templatescreate · list · get · update · delete
analyticsdeliverability · activity
webhookEndpointscreate · list · setStatus · delete · deliveries · test
webhooks.constructEvent(body, header, secret, opts?) => WebhookEvent
webhooks.verify(body, header, secret, opts?) => boolean

Errors

Every non-2xx response throws an AtrixMailError carrying the API's stable code, the HTTP status, and — for validation errors — a fields map. Network failures throw with status: 0 and code: "network_error".

ts
import { AtrixMailError } from "@atrix-mail/sdk";

try {
  await mail.emails.send(payload);
} catch (err) {
  if (err instanceof AtrixMailError) {
    if (err.is("domain_not_verified")) { /* fix the from domain */ }
    if (err.isRetryable) {
      // back off; err.retryAfter has the seconds from Retry-After on a 429
    }
    console.error(err.code, err.status, err.fields);
  }
}

Verify webhooks

webhooks.constructEvent verifies the atrix-signature header (HMAC + timestamp tolerance) and returns a typed event; narrow on event.type to get typed data. See Webhooks for the full example.

ts
const event = mail.webhooks.constructEvent(rawBody, signatureHeader, webhookSecret);
if (event.type === "email.delivered") {
  console.log(event.data.email_id);
}
Scope. The SDK is the API-key surface: sending, retrieving, and webhook verification. Domain, mailbox, and webhook management live in the dashboard (session-authenticated) and are not part of the client — see Authentication.