API v1.0

SendPigeon Documentation

Everything you need to send transactional emails. SDKs for Node.js, Python, Go, PHP and REST API.

SDKs

Full reference for all official SDKs.

Send Options

OptionTypeDescription
from*stringSender email. Domain must be verified.
to*string | string[]Recipient email(s).
subjectstringEmail subject. Required unless using templateId.
htmlstringHTML body content.
textstringPlain text body.
ccstring | string[]CC recipients.
bccstring | string[]BCC recipients.
replyTostringReply-to address.
templateIdstringTemplate ID to use.
variablesobjectVariables for template substitution.
attachmentsAttachment[]File attachments. Max 7MB each, 25MB total.
tagsstring[]Tags for filtering/analytics. Max 5 tags.
metadataobjectCustom key-value pairs. Returned in webhooks.
headersobjectCustom headers (X-Priority, List-Unsubscribe, etc).
scheduledAtstringISO 8601 datetime to send. Max 30 days ahead.

Attachments

Attach files via base64 content or URL (fetched server-side).

TypeScripttypescript
// Base64 content
const { error } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Your invoice",
html: "<p>See attached invoice.</p>",
attachments: [{
filename: "invoice.pdf",
content: fs.readFileSync("invoice.pdf").toString("base64")
}]
});

Limits: 7MB per file, 25MB total. Blocked: .exe, .bat, .sh, .dll and other executables.

Scheduled Sending

Schedule emails up to 30 days in advance. Cancel before send time.

TypeScripttypescript
// Schedule for later
const { data } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Reminder",
html: "<p>Don't forget!</p>",
scheduledAt: "2025-06-15T10:00:00Z"
});
// { id: "em_abc123", status: "scheduled", scheduledAt: "..." }
// Cancel scheduled email
await pigeon.emails.cancel(data.id);

Tags & Metadata

Track emails with tags and metadata. Both are returned in webhooks and email details.

TypeScripttypescript
const { data } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Order confirmed",
html: "<p>Your order is confirmed.</p>",
tags: ["order", "confirmation"],
metadata: { orderId: "12345", userId: "abc" },
headers: { "X-Priority": "1" }
});

Get Email Status

Check delivery status, tags, and metadata of a sent email.

TypeScripttypescript
const { data } = await pigeon.emails.get("em_abc123");
console.log(data.status); // "delivered"
console.log(data.tags); // ["order", "confirmation"]
console.log(data.metadata); // { orderId: "12345" }

Templates

Templates use {{variable}} syntax. Create them in the dashboard or via SDK.

TypeScripttypescript
// Create a template
const { data: template } = await pigeon.templates.create({
name: "welcome-email",
subject: "Welcome {{name}}!",
html: "<p>Hello {{name}}, welcome to {{company}}!</p>"
});
// Send with template
const { error } = await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
templateId: template.id,
variables: { name: "Johan", company: "Acme" }
});

Webhooks

Receive real-time notifications for email events. Configure your webhook URL in the dashboard settings.

EventDescription
email.deliveredEmail successfully delivered to recipient
email.bouncedEmail bounced (hard or soft bounce)
email.complainedRecipient marked email as spam
email.openedRecipient opened the email (requires tracking enabled)
email.clickedRecipient clicked a link (requires tracking enabled)
Webhook Payloadjson
// Webhook payload
{
"event": "email.delivered",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"emailId": "em_abc123",
"toAddress": "user@example.com",
"fromAddress": "hello@yourdomain.com",
"subject": "Welcome!"
}
}
// For bounces, includes bounceType
// For complaints, includes complaintType

Webhook Verification

Verify webhook signatures using the SDK helper.

TypeScripttypescript
import { verifyWebhook } from "sendpigeon";
app.post("/webhook", async (req, res) => {
const result = await verifyWebhook({
payload: req.body,
signature: req.headers["x-webhook-signature"],
timestamp: req.headers["x-webhook-timestamp"],
secret: process.env.WEBHOOK_SECRET,
});
if (!result.valid) {
return res.status(400).json({ error: result.error });
}
const { event, data } = result.payload;
// Handle event...
res.sendStatus(200);
});

Using with React Email

Build emails with React components using React Email, then render to HTML before sending.

TypeScripttsx
import { render } from "@react-email/render";
import { SendPigeon } from "sendpigeon";
import { WelcomeEmail } from "./emails/welcome";
const pigeon = new SendPigeon("sp_live_your_api_key");
const html = await render(<WelcomeEmail name="Johan" />);
await pigeon.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Welcome!",
html,
});

Configuration

Customize client behavior with options.

TypeScripttypescript
const pigeon = new SendPigeon("sp_live_your_api_key", {
timeout: 30000, // request timeout in ms (default: 30s)
maxRetries: 2, // retry on 429/5xx (default: 2, max: 5)
debug: true, // log requests/responses to console
});

Retries use exponential backoff (500ms, 1s, 2s, 4s, 8s) and respect Retry-After headers.

Batch Sending

Send up to 100 emails in a single request. Each email is processed independently.

TypeScripttypescript
const { data, error } = await pigeon.sendBatch([
{
from: "hello@yourdomain.com",
to: "user1@example.com",
subject: "Hello User 1",
html: "<p>Welcome!</p>",
},
{
from: "hello@yourdomain.com",
to: "user2@example.com",
subject: "Hello User 2",
html: "<p>Welcome!</p>",
},
]);
// Check results
console.log(data.summary); // { total: 2, sent: 2, failed: 0 }
for (const result of data.data) {
if (result.status === "sent") {
console.log(`Email ${result.index} sent: ${result.id}`);
} else {
console.log(`Email ${result.index} failed: ${result.error.message}`);
}
}

Domain Management

Manage sending domains programmatically.

TypeScripttypescript
// List domains
const { data: domains } = await pigeon.domains.list();
// Add a domain
const { data: domain } = await pigeon.domains.create({
name: "mail.example.com"
});
// Get domain with DNS records
const { data: details } = await pigeon.domains.get(domain.id);
console.log(details.dnsRecords); // DKIM, SPF, DMARC records to add
// Verify DNS is configured
const { data: result } = await pigeon.domains.verify(domain.id);
console.log(result.status); // "verified" | "pending" | "failed"
// Delete domain
await pigeon.domains.delete(domain.id);

API Key Management

Create and manage API keys with specific permissions.

TypeScripttypescript
// List API keys
const { data: keys } = await pigeon.apiKeys.list();
// Create a key with options
const { data: newKey } = await pigeon.apiKeys.create({
name: "Production Server",
mode: "live", // "live" | "test"
permission: "sending", // "sending" | "full_access"
expiresAt: "2025-12-31", // optional expiration
domainId: "dom_abc123", // optional domain restriction
});
console.log(newKey.key); // Save this - only shown once!
// Delete a key
await pigeon.apiKeys.delete(newKey.id);

Local Development

Catch emails locally during development with @sendpigeon-sdk/cli. No real emails sent.

Terminalbash
# Terminal 1: Start the dev server
npx @sendpigeon-sdk/cli dev
# Terminal 2: Run your app with dev mode enabled
SENDPIGEON_DEV=true npm run dev

When SENDPIGEON_DEV=true is set, the SDK routes to localhost:4100 instead of production.

TypeScripttypescript
// The SDK auto-detects dev mode
const client = new SendPigeon("sp_test_xxx");
// When SENDPIGEON_DEV=true, this goes to localhost:4100
await client.send({
from: "hello@yourdomain.com",
to: "user@example.com",
subject: "Test",
html: "<p>Caught locally!</p>"
});
// You'll see in your logs:
// [SendPigeon] Dev mode → http://localhost:4100

UI: View caught emails at http://localhost:4100. Learn more →