Sendry Docs
SDKs

TypeScript SDK

Official TypeScript/Node.js SDK for the Sendry email API.

Installation

npm install sendry-sdk
# or
bun add sendry-sdk
# or
pnpm add sendry-sdk

The SDK is written in TypeScript and works with Node.js 18+, Bun, Deno, and edge runtimes.

Initialization

import { Sendry } from "sendry-sdk";

// Simple initialization with API key string
const sendry = new Sendry("sn_live_your_api_key");

// Or with configuration options
const sendry = new Sendry({
  apiKey: process.env.SENDRY_API_KEY!,
  baseUrl: "https://api.sendry.online", // Override base URL
  timeout: 30000,                    // Request timeout in ms (default: 30s)
  retries: 2,                        // Retry count for 5xx errors (default: 2)
  headers: { "X-App-Version": "1.0" }, // Extra headers on every request
});

Emails

// Send a single email
const { id, status } = await sendry.emails.send({
  from: "Acme <hello@acme.com>",
  to: "user@example.com",
  subject: "Welcome!",
  html: "<h1>Welcome to Acme!</h1>",
});

// Send to multiple recipients
await sendry.emails.send({
  from: "hello@acme.com",
  to: ["alice@example.com", "bob@example.com"],
  subject: "Team announcement",
  html: "<p>Big news...</p>",
});

// Send with attachments
await sendry.emails.send({
  from: "billing@acme.com",
  to: "user@example.com",
  subject: "Your invoice",
  html: "<p>Please find your invoice attached.</p>",
  attachments: [
    {
      filename: "invoice.pdf",
      content: Buffer.from(pdfBytes).toString("base64"),
      content_type: "application/pdf",
    },
  ],
});

// Schedule a future send
await sendry.emails.send({
  from: "hello@acme.com",
  to: "user@example.com",
  subject: "Reminder",
  html: "<p>Don't forget your appointment tomorrow.</p>",
  scheduled_at: "2025-03-15T09:00:00Z",
});

// Send using a template
await sendry.emails.send({
  from: "hello@acme.com",
  to: "user@example.com",
  template_id: "tmpl_abc123",
  variables: { name: "Alice", plan: "Pro" },
});

// Send up to 100 emails in one request
const { data } = await sendry.emails.sendBatch({
  from: "hello@acme.com",
  subject: "Order update",
  emails: [
    { to: "alice@example.com", html: "<p>Alice, your order shipped!</p>" },
    { to: "bob@example.com", html: "<p>Bob, your order shipped!</p>" },
  ],
});

// Send marketing email (includes List-Unsubscribe)
await sendry.emails.sendMarketing({
  from: "Newsletter <news@acme.com>",
  to: "user@example.com",
  subject: "March Update",
  html: "<p>Here's what happened this month...</p>",
  unsubscribe_url: "https://acme.com/unsubscribe?id=abc123",
});

// Get email status
const email = await sendry.emails.get("em_abc123");
console.log(email.status); // "delivered"

// List emails with filtering
const { data, has_more, next_cursor } = await sendry.emails.list({
  limit: 25,
  status: "delivered",
});

// Paginate
if (has_more) {
  const nextPage = await sendry.emails.list({ cursor: next_cursor! });
}

// Cancel a scheduled/queued email
await sendry.emails.cancel("em_abc123");

Domains

// Add a domain (returns DNS records to configure)
const domain = await sendry.domains.create({ name: "acme.com" });
console.log(domain.dns_records); // Array of DNS records to add

// Verify a domain after DNS propagation
const result = await sendry.domains.verify("dom_abc123");
console.log(result.status); // "verified"
console.log(result.spf_verified, result.dkim_verified, result.dmarc_verified);

// List all domains
const { data } = await sendry.domains.list();

// Delete a domain
await sendry.domains.remove("dom_abc123");

Templates

// Create a template
const template = await sendry.templates.create({
  name: "Welcome Email",
  subject: "Welcome, {{name}}!",
  html: "<h1>Hi {{name}},</h1><p>Welcome to {{company}}!</p>",
});

// Render a preview
const preview = await sendry.templates.render("tmpl_abc123", {
  variables: { name: "Alice", company: "Acme" },
});
console.log(preview.subject); // "Welcome, Alice!"

// List, get, update, delete
await sendry.templates.list();
await sendry.templates.get("tmpl_abc123");
await sendry.templates.update("tmpl_abc123", { subject: "New subject" });
await sendry.templates.remove("tmpl_abc123");

API keys

// Create a new API key (raw key only returned once)
const { key, id } = await sendry.apiKeys.create({
  name: "Production Sender",
  scope: "sending_access",
});
console.log(key); // "sn_live_..." — save this!

// List all keys (secrets not included)
const { data } = await sendry.apiKeys.list();

// Delete a key
await sendry.apiKeys.remove("ak_abc123");

Webhooks

// Create a webhook
const webhook = await sendry.webhooks.create({
  url: "https://yourapp.com/webhooks/sendry",
  events: ["email.delivered", "email.bounced"],
});
console.log(webhook.secret); // Save this for verification!

// Manage webhooks
await sendry.webhooks.list();
await sendry.webhooks.get("wh_abc123");
await sendry.webhooks.update("wh_abc123", { active: false });
await sendry.webhooks.remove("wh_abc123");

Analytics

const { summary, timeseries } = await sendry.analytics.stats({
  from: "2025-01-01",
  to: "2025-01-31",
  granularity: "week",
});

const logs = await sendry.analytics.logs({
  email_id: "em_abc123",
  type: "bounced",
});

Suppression

await sendry.suppression.list();
await sendry.suppression.add({ email: "bad@example.com", reason: "hard_bounce" });
await sendry.suppression.remove("bad@example.com");

Segments

const seg = await sendry.segments.create({ name: "High-Value Users" });
await sendry.segments.addContacts(seg.id, { contact_ids: ["ct_1", "ct_2"] });
const { data } = await sendry.segments.listContacts(seg.id);

Topics

const topic = await sendry.topics.create({
  name: "product-updates",
  opt_in_required: true,
});
await sendry.topics.subscribe(topic.id, "user@example.com");

Contact properties

await sendry.contactProperties.create({ name: "plan", label: "Plan", type: "string" });
await sendry.contactProperties.setValues("ct_123", { plan: "pro" });

Deliverability insights

const insights = await sendry.deliverabilityInsights.forEmail("em_abc123");
// insights.score — 0-100; insights.checks — per-rule feedback
const { url, expires_at } = await sendry.emailSharing.create("em_abc123");

Attachment download

const file = await sendry.attachments.download("em_abc123", 0);
await Bun.write(file.filename ?? "attachment.bin", file.content);

Per-domain tracking toggles

await sendry.tracking.setForDomain("dom_abc123", { opens: true, clicks: false });

Exports (CSV)

const { id } = await sendry.exports.create({ resource: "contacts" });
const csv = await sendry.exports.download(id);  // poll exports.get() until ready

Postmaster (Gmail Postmaster Tools)

const { data } = await sendry.postmaster.metrics({ domain_id: "dom_abc" });
await sendry.postmaster.sync();

Dead-letter queue (admin)

const { data } = await sendry.dlq.list({ queue: "email-sender" });
await sendry.dlq.retry(data[0].id);

Error handling

import { Sendry, ApiError, AuthenticationError, RateLimitError, ValidationError } from "sendry-sdk";

try {
  await sendry.emails.send({ ... });
} catch (error) {
  if (error instanceof AuthenticationError) {
    // 401 — invalid or expired API key
    console.error("Check your SENDRY_API_KEY");
  } else if (error instanceof RateLimitError) {
    // 429 — back off and retry
    const retryAfter = error.retryAfter; // seconds to wait
    await sleep(retryAfter * 1000);
  } else if (error instanceof ValidationError) {
    // 422 — request body failed validation
    console.error(error.details);
  } else if (error instanceof ApiError) {
    // All other API errors
    console.error(error.statusCode, error.code, error.message);
  }
}

See Error Codes for the full list of error codes.

TypeScript types

All request and response types are exported:

import type {
  SendEmailRequest,
  BatchEmailRequest,
  EmailItem,
  DomainItem,
  WebhookItem,
  AnalyticsStats,
} from "sendry-sdk";

Source

github.com/sendry-dev/sendry-js — MIT licensed. Published as sendry-sdk on npm.

On this page