{"_id":"@aradox/mailer","name":"@aradox/mailer","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@aradox/mailer","version":"1.0.0","description":"Node.js SDK for the Mailer email service","license":"MIT","main":"dist/index.js","types":"dist/index.d.ts","keywords":["email","smtp","mailer","transactional","webhook","dkim"],"publishConfig":{"access":"public"},"scripts":{"build":"tsc -p .","test":"node --test --import tsx test/*.test.ts","prepublishOnly":"npm run build && npm test"},"engines":{"node":">=18"},"devDependencies":{"tsx":"^4.19.2","typescript":"^5.7.2","@types/node":"^22.10.5"},"gitHead":"34752b03c996dfba230a1eafd9b2bb60dd2f956f","_id":"@aradox/mailer@1.0.0","_nodeVersion":"24.2.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-HZUikQXX01F/TsQq8Yll1qzW58bShbKOfls4KKch4Xabtgzpm0ak34mjhprk4Wg9+0JZ2XYGHADY6ImJoXK3DQ==","shasum":"a9bd0366081a65737db5ea7c7c2289985de6d158","tarball":"https://registry.npmjs.org/@aradox/mailer/-/mailer-1.0.0.tgz","fileCount":5,"unpackedSize":29085,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFbyfu3k9+QSmDNSuXFYUk9CFzaO+e5oGHgZlrEuN9gSAiAHIw5uKKhBCTTS9TaiyTUdYEzK4w0qhpMfGh/gcgX9PA=="}]},"_npmUser":{"name":"aradox","email":"info@studioprimary.com"},"directories":{},"maintainers":[{"name":"aradox","email":"info@studioprimary.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mailer_1.0.0_1777489291976_0.7615249378260558"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T19:01:31.817Z","1.0.0":"2026-04-29T19:01:32.132Z","modified":"2026-04-29T19:01:32.562Z"},"maintainers":[{"name":"aradox","email":"info@studioprimary.com"}],"description":"Node.js SDK for the Mailer email service","keywords":["email","smtp","mailer","transactional","webhook","dkim"],"license":"MIT","readme":"# @aradox/mailer\n\nNode.js SDK for the [Mailer](https://github.com/studioprimary/mailer) email service.\nZero runtime deps — uses the global `fetch` (Node 18+).\n\n## Install\n\n```bash\nnpm install @aradox/mailer\n```\n\n## Quick start\n\n```ts\nimport { Mailer } from \"@aradox/mailer\";\n\nconst mailer = new Mailer({\n  apiKey: process.env.MAILER_API_KEY!,        // app_xxxxxxxxxxxxxx\n  baseURL: process.env.MAILER_URL,            // defaults to https://mailer.example.com\n});\n\n// Single send\nconst { id } = await mailer.emails.send({\n  from: \"Acme <noreply@mail.example.com>\",\n  to: \"user@example.com\",\n  subject: \"Welcome\",\n  html: \"<p>Hi!</p>\",\n  text: \"Hi!\",\n});\n```\n\n## Sending\n\n```ts\n// Send by template + variables\nawait mailer.emails.send({\n  from: \"Acme <noreply@mail.example.com>\",\n  to: \"user@example.com\",\n  template: \"welcome\",\n  variables: { FirstName: \"Sam\", ResetURL: \"https://app.example.com/r/abc\" },\n  subscription_group: \"marketing\",\n});\n\n// Batch (up to 500)\nawait mailer.emails.batch([\n  { from, to: \"a@x.com\", subject: \"a\", text: \"a\" },\n  { from, to: \"b@x.com\", subject: \"b\", text: \"b\" },\n]);\n\n// Attachments (Buffer or base64 string)\nimport { readFile } from \"node:fs/promises\";\nawait mailer.emails.send({\n  from,\n  to: \"user@example.com\",\n  subject: \"Your invoice\",\n  html: \"<p>See attached.</p>\",\n  attachments: [\n    {\n      filename: \"invoice.pdf\",\n      content: await readFile(\"./invoice.pdf\"), // Buffer auto-base64'd\n      content_type: \"application/pdf\",\n    },\n  ],\n});\n\n// Inspect\nconst events = await mailer.emails.events(id);\n```\n\n## Idempotency\n\nPass `idempotency_key` and the SDK forwards it both as the JSON field and\nthe `Idempotency-Key` HTTP header. Calling twice with the same key returns\nthe same email — safe to retry on network blips, useful when wiring up\norder-confirmation flows.\n\n```ts\nawait mailer.emails.send({\n  from, to: \"user@example.com\", subject: \"Welcome\", html: \"<p>Hi</p>\",\n  idempotency_key: \"order-123-welcome\",\n});\n```\n\n## Automatic retries\n\nTransient failures (HTTP 429, 5xx, network errors) are retried up to 3\ntimes by default with exponential backoff + full jitter, capped at 8s\nper delay. The `Retry-After` header is honoured when present.\n\n```ts\nconst mailer = new Mailer({\n  apiKey: \"...\",\n  maxRetries: 5,         // default 3\n  retryBaseDelayMs: 500, // default 250\n});\n```\n\n## Webhook signature verification\n\nInbound webhooks are signed with HMAC-SHA256. Verify them before trusting\nthe payload:\n\n```ts\nimport { Mailer, WebhookVerificationError } from \"@aradox/mailer\";\n\n// Express / Fastify / etc.\napp.post(\"/mailer-webhook\", async (req, res) => {\n  const rawBody = req.rawBody as string;  // capture raw body — see below\n  try {\n    const event = mailer.webhooks.verify({\n      body: rawBody,\n      secret: process.env.WEBHOOK_SECRET!,\n      headers: req.headers,\n    });\n    // event.event_type === \"email.delivered\" | \"email.bounced\" | ...\n    await handleEvent(event);\n    res.sendStatus(204);\n  } catch (e) {\n    if (e instanceof WebhookVerificationError) return res.status(400).send(e.message);\n    throw e;\n  }\n});\n```\n\n> **Important**: pass the *raw* request body string. If your framework\n> JSON-parses the body before you see it, the signature will fail. In\n> Express, use `express.raw({ type: \"application/json\" })` and capture\n> `req.body.toString(\"utf8\")`.\n\nThe verifier rejects:\n- Missing or malformed `X-Mailer-Signature` header\n- Timestamps outside a 5-minute tolerance (configurable via `toleranceSeconds`)\n- Signatures that don't match\n- Non-JSON bodies\n\n## Errors\n\nFailed requests throw a `MailerError` carrying `status`, `body`, `code`\n(when present), and `requestId` (echoed from `X-Request-Id`).\n\n```ts\nimport { MailerError } from \"@aradox/mailer\";\n\ntry {\n  await mailer.emails.send({ from: \"bad\", to: \"user@example.com\" });\n} catch (e) {\n  if (e instanceof MailerError) {\n    console.error(\"send failed\", { status: e.status, code: e.code, requestId: e.requestId });\n    if (e.status === 429) /* rate limited — retry later (or rely on auto-retry) */;\n    if (e.status === 422 && e.code === \"suppressed\") /* recipient is on the suppression list */;\n  }\n}\n```\n\n## Tests\n\n```bash\nnpm test\n```\n","readmeFilename":"README.md","_rev":"1-683f0d8ed9e4ab49ba4de51d4734b426"}