JSPM

sendfully

0.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q75462F
  • License MIT

Sendfully Node.js SDK

Package Exports

  • sendfully

Readme

Sendfully Node.js SDK

The official Node.js SDK for Sendfully.

Installation

npm install sendfully
# or
pnpm add sendfully
# or
yarn add sendfully

Requires Node.js 20.3 or later. Zero runtime dependencies.

Quick start

import { Sendfully } from "sendfully";

const client = new Sendfully({ apiKey: process.env.SENDFULLY_API_KEY });

// Send a transactional email from a published template
await client.emails.send({
  templateId: "welcome_email_a1b2",
  to: "user@example.com",
  variables: { first_name: "Ada" },
});

// Or send inline HTML
await client.emails.send({
  from: "Acme <hello@acme.com>",
  to: "user@example.com",
  subject: "Hello from Acme",
  html: "<p>Welcome!</p>",
});

The apiKey option falls back to the SENDFULLY_API_KEY environment variable when omitted.

Contacts

// Create
const { id } = await client.contacts.create({
  email: "user@example.com",
  firstName: "Ada",
  subscribed: true,
});

// Look up by UUID or email
const contact = await client.contacts.get("user@example.com");

// Update (PATCH semantics: only the fields you pass are changed)
await client.contacts.update(id, { subscribed: false });

// Delete
await client.contacts.delete(id);

Emails

A few common scenarios for emails.send. Sending from a published template is the main approach, but you can also provide inline content directly.

Templated

await client.emails.send({
  templateId: "order-confirmation",
  to: "buyer@example.com",
  variables: { orderId: "1234", total: "$42.00" },
});

Inline content

await client.emails.send({
  from: "Acme <hello@acme.com>",
  to: ["a@example.com", "b@example.com"],
  subject: "Weekly digest",
  html: "<h1>This week</h1>...",
  text: "This week: ...", // optional, auto-generated from html when omitted
});

Scheduling

await client.emails.send({
  templateId: "reminder",
  to: "user@example.com",
  scheduledAt: new Date("2026-05-01T09:00:00Z"),
});

Attachments

import { readFileSync } from "node:fs";

await client.emails.send({
  from: "hello@acme.com",
  to: "user@example.com",
  subject: "Your receipt",
  html: "<p>Receipt attached.</p>",
  attachments: [
    {
      filename: "receipt.pdf",
      content: readFileSync("./receipt.pdf").toString("base64"),
    },
    // Or fetch from a public URL:
    { filename: "logo.png", path: "https://cdn.example.com/logo.png" },
  ],
});

Error handling

Every error thrown by the SDK extends SendfullyError. See the API documentation for the full list of error classes and the status codes they map to.

Configuration

apiKey is the only required setting. The rest have defaults you can override if you need to.

const client = new Sendfully({
  apiKey: "sf_...", // required (or set SENDFULLY_API_KEY)
  baseUrl: "https://api.sendfully.com",
  timeout: 60_000, // ms, default 60s
  maxRetries: 2,
  headers: { "X-App": "myapp" }, // merged into every request
  fetch: customFetch, // override the fetch implementation
});

Every resource method also accepts a trailing options object to override these defaults per-call:

await client.emails.send(
  { templateId: "t", to: "u@example.com" },
  { timeout: 5_000, signal: controller.signal },
);

Retries

GET, PATCH, and DELETE are retried automatically on transient failures. POST is only retried when you pass idempotencyKey, since otherwise a retry could send the same email twice.

Pass idempotencyKey on emails.send to make a send safely retryable. If you call emails.send again with the same key within 24 hours, the API returns the original response instead of sending a second time.

await client.emails.send(
  { templateId: "welcome_email_a1b2", to: "user@example.com" },
  { idempotencyKey: "welcome-user-1234" },
);

License

MIT