Documentation
Send your first email.
Verify a domain, create an API key, and send — through the typed Node SDK or straight over HTTP from any language.
Introduction
Sendridge is a transactional email platform for developers. You verify a sending domain, grab an API key, and send email through a simple HTTPS API — Sendridge handles queuing, delivery, retries, and tracking (opens, clicks, bounces, complaints).
There are two ways to integrate: the official Node.js SDK (recommended) or the REST API directly from any language.
Quickstart
- 1Create an account and confirm your email address using the link we send you. Your dashboard opens once you do — if the email doesn't arrive, check your spam folder or use Resend verification email on the verification page.
- 2Verify your sending domain (Dashboard → Domains). Sendridge gives you DNS records to add; verification usually completes in minutes.
- 3Create an API key (Dashboard → API Keys). Keys look like
sr_…and are shown once — store yours securely. - 4Install the SDK and send a test email to your registered address:
npm install sendridgeimport { Sendridge } from "sendridge";
const sendridge = new Sendridge(process.env.SENDRIDGE_API_KEY);
const { id } = await sendridge.emails.send({
from: "Acme <you@yourdomain.com>",
to: "your-registered-email@example.com",
subject: "Welcome aboard!",
html: "<h1>Hello 👋</h1><p>Thanks for signing up.</p>",
});
console.log(`Queued email ${id}`);Sending is asynchronous: the API responds immediately with status: "queued", then Sendridge delivers in the background. Use emails.get(id) to follow the delivery status.
Sandbox and going live
Every new account starts in sandbox mode. You can send test emails from a verified domain to your own registered email address while setting up. Sends to other recipients become available after your account and sending domain are approved.
Before you can request live sending, confirm your account email, verify a domain's ownership and DKIM records, complete your organisation name and postal address in Settings, and send a test email from that verified domain to your registered address. Custom MAIL FROM is recommended but does not block your request.
The Sending access page shows your checklist and the Request live sending form. Tell us what you'll send, your business type, and your HTTPS website, then accept the sending rules. Reviews usually take a few minutes; you can keep testing with your own address while you wait.
If your request is rejected, we'll show the reason in your dashboard and email it to you. Fix the issue and resubmit the form. When your account is approved, the verified domains included in that approval can send to other recipients. A domain you verify later needs its own quick approval from the Domains page; your already approved domains keep working while it is reviewed.
Authentication
Every request is authenticated with your API key as a Bearer token. The SDK does this for you; for direct REST calls, send the header yourself:
Authorization: Bearer sr_xxxxxxxxxxxxxxxxBest practice: keep the key in an environment variable. The SDK automatically reads SENDRIDGE_API_KEY when you don't pass a key to the constructor.
Keys can be scoped with permissions (read, write, send), restricted to IP allowlists, given expiry dates and per-minute rate caps, and rotated at any time — all from the dashboard. A rotated or revoked key stops working immediately.
SDK — Installation
npm install sendridgeRequires Node.js 18+, or any runtime with fetch — including the Next.js Edge runtime and Nuxt's edge presets. The package ships TypeScript types, works with both import (ESM) and require (CommonJS), and has zero runtime dependencies. Framework guides: Next.js and Nuxt.
// ESM / TypeScript
import { Sendridge } from "sendridge";
// CommonJS
const { Sendridge } = require("sendridge");
const sendridge = new Sendridge(); // reads SENDRIDGE_API_KEYSDK — Send an email
const result = await sendridge.emails.send({
from: "Acme Billing <billing@yourdomain.com>", // name shown in the inbox; domain must be verified
to: ["a@example.com", "b@example.com"], // string or array, max 50
subject: "Your invoice",
html: "<p>Invoice attached below.</p>",
text: "Invoice attached below.", // plain-text alternative
replyTo: "support@yourdomain.com",
tags: { type: "invoice" },
metadata: { invoiceId: "INV-2026-0042" },
});
// result:
// {
// id: "665f1c2e9b1d2a0012345678",
// status: "queued",
// to: ["a@example.com", "b@example.com"],
// from: "Acme Billing <billing@yourdomain.com>",
// subject: "Your invoice",
// createdAt: "2026-07-15T10:30:00.000Z"
// }| Field | Type | Required | Description |
|---|---|---|---|
| from | string | Yes | Sender, as "Your Name <you@yourdomain.com>" or a bare address. Inboxes show the name as the sender; without one they show the part before the @ (e.g. "info"). The domain must be verified in your dashboard. |
| to | string | string[] | Yes | One recipient, or up to 50. |
| subject | string | Yes | Subject line (max 998 chars). |
| html | string | One of html/text | HTML body. |
| text | string | One of html/text | Plain-text body. |
| replyTo | string | No | Reply-To address. Accepts the same "Name <address>" form. |
| tags | Record<string, string> | No | String tags for your own filtering. |
| metadata | Record<string, unknown> | No | Arbitrary JSON stored with the email. |
SDK — Retrieve an email
const email = await sendridge.emails.get("665f1c2e9b1d2a0012345678");
// email.status is one of:
// "queued" | "sending" | "sent" | "delivered" | "bounced" | "failed" | "complained"
console.log(email.status, email.openCount, email.clickCount);The returned object includes the full body (html/text), delivery status, open and click counts, your tags and metadata, and an errorMessage when delivery failed.
SDK — List emails
const { data, pagination } = await sendridge.emails.list({ page: 1, limit: 20 });
for (const email of data) {
console.log(email.id, email.subject, email.status);
}
// pagination: { page: 1, limit: 20, total: 132 }SDK — Error handling
Every failure throws a typed subclass of SendridgeError, so you can branch on the exact failure mode:
import {
SendridgeError,
ValidationError, // 400 — bad payload; err.details has field errors
AuthenticationError, // 401 — missing / invalid / revoked / expired key
PermissionError, // 403 — e.g. sandbox or domain approval restriction
NotFoundError, // 404 — unknown email id
RateLimitError, // 429 — rate limit or quota exceeded
ServerError, // 5xx — Sendridge-side problem
TimeoutError, // request exceeded the timeout
NetworkError, // DNS / connection failure
} from "sendridge";
try {
await sendridge.emails.send({ /* ... */ });
} catch (err) {
if (err instanceof RateLimitError) {
// back off, then try again
} else if (err instanceof ValidationError) {
console.error(err.message, err.details);
} else if (err instanceof SendridgeError) {
console.error(err.statusCode, err.message);
} else {
throw err;
}
}Idempotent reads (get, list) automatically retry up to twice on network failures and 5xx/429 responses, with exponential backoff. Sends are never retried.
A send blocked by sandbox mode (sandbox_restricted) or a domain awaiting approval (domain_not_approved) throws PermissionError. Its messageexplains the restriction and where to request approval.
SDK — Configuration
const sendridge = new Sendridge("sr_...", {
baseUrl: "https://api.sendridge.com", // override for local dev or staging
timeoutMs: 30_000, // per-request timeout (default 30s)
maxRetries: 2, // GET-only automatic retries (default 2)
});| Field | Type | Required | Description |
|---|---|---|---|
| SENDRIDGE_API_KEY | env | No | Used when no key is passed to the constructor. |
| SENDRIDGE_BASE_URL | env | No | Used when no baseUrl option is passed — point the SDK at a local or staging API. |
Every request is sent with cache: "no-store", so framework fetch caches never serve a stale email status. The SDK refuses to start in a browser, where your API key would be visible to every visitor.
Next.js
Works in the App Router and the Pages Router, on the Node.js and Edge runtimes. Use it in server code — route handlers, server actions, getServerSideProps — never in a "use client" component. Add your key to .env.local:
SENDRIDGE_API_KEY=sr_xxxxxxxxxxxxxxxxA route handler:
// app/api/send/route.ts
import { Sendridge } from "sendridge";
const sendridge = new Sendridge(); // reads SENDRIDGE_API_KEY
export async function POST(request: Request) {
const { email } = await request.json();
const { id } = await sendridge.emails.send({
from: "Acme <hello@yourdomain.com>",
to: email,
subject: "Welcome to Acme",
html: "<p>Thanks for signing up.</p>",
});
return Response.json({ id });
}
Or a server action:
// app/actions.ts
"use server";
import { Sendridge } from "sendridge";
const sendridge = new Sendridge();
export async function sendWelcome(formData: FormData) {
const email = String(formData.get("email"));
await sendridge.emails.send({
from: "Acme <hello@yourdomain.com>",
to: email,
subject: "Welcome to Acme",
html: "<p>Thanks for signing up.</p>",
});
}
To run a route on the Edge runtime, add export const runtime = "edge"; to it. Nothing else changes. Creating the client at the top of a file is safe: a missing key is only reported when you send, so next build never fails because a build machine lacks SENDRIDGE_API_KEY.
Nuxt
Works with Nuxt 3 and Nuxt 4 in server routes, on Node and edge deployment presets. Keep the key in server-only runtime config:
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// Server-only. Set it with the NUXT_SENDRIDGE_API_KEY environment variable.
sendridgeApiKey: "",
},
});
NUXT_SENDRIDGE_API_KEY=sr_xxxxxxxxxxxxxxxxThen send from a server route:
// server/api/send.post.ts
import { Sendridge } from "sendridge";
export default defineEventHandler(async (event) => {
const { email } = await readBody<{ email: string }>(event);
const sendridge = new Sendridge(useRuntimeConfig(event).sendridgeApiKey);
const { id } = await sendridge.emails.send({
from: "Acme <hello@yourdomain.com>",
to: email,
subject: "Welcome to Acme",
html: "<p>Thanks for signing up.</p>",
});
return { id };
});
REST API — Overview
Prefer another language? The REST API is the same surface the SDK wraps. All endpoints accept and return JSON.
Base URL: https://api.sendridge.com
Auth: Authorization: Bearer sr_xxxxxxxxxxxxxxxxSuccessful responses wrap the payload in a data field; errors return { error, message, code?, details? }.
POST /v1/emails/send
Queues an email for delivery. Responds 202 Accepted.
curl -X POST https://api.sendridge.com/v1/emails/send \
-H "Authorization: Bearer $SENDRIDGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "you@yourdomain.com",
"to": "customer@example.com",
"subject": "Welcome!",
"html": "<h1>Hello</h1>"
}'{
"data": {
"id": "665f1c2e9b1d2a0012345678",
"status": "queued",
"to": ["customer@example.com"],
"from": "you@yourdomain.com",
"subject": "Welcome!",
"createdAt": "2026-07-15T10:30:00.000Z"
},
"message": "Email queued for delivery"
}GET /v1/emails/:id
Returns a single email you own, including delivery status and engagement counts.
curl https://api.sendridge.com/v1/emails/665f1c2e9b1d2a0012345678 \
-H "Authorization: Bearer $SENDRIDGE_API_KEY"{
"data": {
"_id": "665f1c2e9b1d2a0012345678",
"to": ["customer@example.com"],
"from": "you@yourdomain.com",
"subject": "Welcome!",
"status": "delivered",
"openCount": 2,
"clickCount": 1,
"createdAt": "2026-07-15T10:30:00.000Z",
"updatedAt": "2026-07-15T10:30:04.000Z"
}
}GET /v1/emails
Paginated list of your emails, newest first. Query params: page (default 1) and limit (default 20).
curl "https://api.sendridge.com/v1/emails?page=1&limit=20" \
-H "Authorization: Bearer $SENDRIDGE_API_KEY"REST API — Errors
| Status | Meaning | Typical causes |
|---|---|---|
| 400 | Validation error | Missing/invalid fields; details lists per-field errors |
| 401 | Unauthorized | Missing, invalid, revoked, or expired API key |
| 403 | Forbidden | Unverified sending domain, key permissions, IP allowlist, plan restrictions |
| 403 sandbox_restricted | Account is not live | A send to another recipient is blocked; test sends to your registered address still work. Request live sending in the dashboard. |
| 403 domain_not_approved | Domain is not approved | A live account sent to another recipient from an unapproved domain. Request approval on the Domains page. |
| 404 | Not found | Unknown email id |
| 429 | Too many requests | Per-minute/hour rate limit or monthly quota exceeded |
| 5xx | Server error | Sendridge-side problem — safe to retry idempotent requests |
{
"error": "Validation Error",
"message": "Invalid request data",
"details": { "from": ["Invalid email"] }
}For these two 403 responses, the code field identifies the restriction and message tells you how to resolve it. The SDK surfaces either response as a PermissionError.
Rate limits
Sending limits depend on your plan (per-minute, per-hour, and monthly quotas), and individual API keys can carry an extra per-minute cap set in the dashboard. When you hit a limit the API returns 429 with a message describing which limit was exceeded — back off and retry after a short delay.
Plan limits are listed on the pricing page.
Questions? Open a ticket from your dashboard, or start with the free plan.