Resend-compatible · self-hosted on AWS SES

Email API docs that agents and humans can actually use.

OpenSend keeps the familiar Resend API shape, swaps in os_ keys, and runs on your own infrastructure. Start with the send path, then expand into domains, audience, broadcasts, webhooks, OpenAPI, and MCP.

Quickstart

Base URL: https://api.opensend.com

import { Resend } from "opensend";

const resend = new Resend(process.env.OPENSEND_API_KEY);

const { data, error } = await resend.emails.send({
  from: "OpenSend <onboarding@updates.example.com>",
  to: ["user@example.com"],
  subject: "Hello from OpenSend",
  html: "<strong>It works.</strong>",
});

if (error) throw error;
console.log(data); // { id: "..." }

For production, verify a sending domain first. Use a real recipient only after your domain records are healthy.

OpenSend guides

Everything you need to integrate OpenSend

OpenSend llms.txt

157

markdown docs

OpenAPI 3.0

machine-readable contract

16

highlighted API examples

Docs corpus

Browse the full first-party markdown set

Full index

Emails

Send, batch, cancel, and inspect delivery state.

POST/emails

Send an email

Accepts the Resend-compatible send body. Duplicate Idempotency-Key retries within 24 hours replay the original accepted id.

  • Requires a verified sending domain for production traffic.
await resend.emails.send({
  from: "Acme <onboarding@example.com>",
  to: ["user@example.com"],
  subject: "Welcome",
  html: "<p>Hello world</p>",
});
POST/emails/batch

Send a batch

Queue multiple messages in one request. The batch idempotency key protects the whole accepted envelope.

await resend.emails.sendBatch([
  {
    from: "Acme <news@example.com>",
    to: ["a@example.com"],
    subject: "For A",
    html: "<p>A</p>",
  },
  {
    from: "Acme <news@example.com>",
    to: ["b@example.com"],
    subject: "For B",
    html: "<p>B</p>",
  },
]);
POST/emails/:email_id/cancel

Cancel scheduled email

Cancel a scheduled message before it leaves the queue.

await resend.emails.cancel("email_id");
GET/api/emails/:id

Retrieve email details

OpenSend dashboard/API detail route for status, metadata, and lifecycle events.

const email = await client.emails.get("email_id");

Domains

Add sender domains and verify DKIM, SPF, DMARC, and tracking DNS.

POST/api/domains

Create domain

Add a domain and receive the DNS records required for SES-backed sending.

await client.domains.create({ name: "updates.example.com" });
POST/api/domains/:id/auto-configure

Auto-configure DNS

Operator/self-host Cloudflare token flow for writing the generated records automatically.

await fetch("https://api.opensend.com/api/domains/domain_id/auto-configure", {
  method: "POST",
  headers: { Authorization: "Bearer " + apiKey },
});
GET/api/domains/:id

Retrieve domain

Inspect verification status and copy DNS records.

const domain = await client.domains.get("domain_id");

Audience

Contacts, segments, topics, and contact properties.

POST/contacts

Create contact

Create a contact through the Resend-compatible root alias.

await client.contacts.create({
  email: "jane@example.com",
  firstName: "Jane",
});
POST/segments

Create segment

Create a named audience segment for filtering contacts and campaigns.

await client.segments.create({ name: "Active users" });
GET/segments/:id/contacts

List segment contacts

Fetch contacts that currently match a segment.

const contacts = await client.segments.listContacts("segment_id");

Campaigns

Broadcasts and reusable templates for one-to-many sends.

POST/broadcasts

Create broadcast

Create a draft broadcast with sender, subject, segment, and preview text.

await client.broadcasts.create({
  name: "March newsletter",
  from: "Acme <news@example.com>",
  subject: "March updates",
  segmentId: "segment_id",
});
POST/broadcasts/:id/send

Send broadcast

Send immediately or schedule with the same narrow natural-language window as email sends.

await client.broadcasts.send("broadcast_id", {
  scheduledAt: "in 1 hour",
});
POST/templates

Create template

Store versioned HTML templates that can be rendered for future sends.

await client.templates.create({
  name: "Welcome",
  alias: "welcome",
  subject: "Welcome",
  html: "<p>Hi {{name}}</p>",
});

Platform

API keys, webhooks, OpenAPI, and MCP integrations.

POST/api-keys

Create API key

Create scoped keys. OpenSend keys use the os_ prefix while preserving Resend-compatible API semantics.

await client.apiKeys.create({ name: "Production" });
POST/api/webhooks

Create webhook

Subscribe an HTTPS endpoint to signed delivery and lifecycle events.

await fetch("https://api.opensend.com/api/webhooks", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    endpoint: "https://example.com/webhooks/opensend",
    events: ["email.delivered"],
  }),
});
GET/openapi.json

OpenAPI document

Unauthenticated OpenAPI 3.0 contract for SDKs, generated clients, and audits.

const spec = await fetch("https://api.opensend.com/openapi.json").then((res) => res.json());

LLM docs

Give agents a small, stable map first.

OpenSend exposes the canonical LLM entrypoint at /docs/llms.txt so coding agents can discover the API, SDKs, MCP server, operational setup, and parity notes before generating integration code.

{
  "mcpServers": {
    "opensend": {
      "command": "bun",
      "args": ["/path/to/opensend/packages/mcp/src/stdio.ts"],
      "env": {
        "OPENSEND_API_KEY": "os_YOUR_API_KEY",
        "OPENSEND_API_BASE_URL": "https://api.opensend.com"
      }
    }
  }
}