API v1.0

SendPigeon API Documentation

Everything you need to send transactional emails. Simple REST API and detailed guides.

Quick Start

Send your first email in under a minute.

1
Get your API key

Create an account and grab your API key from the dashboard.

2
Send an email
cURLbash
curl -X POST https://api.sendpigeon.dev/v1/emails \
-H "Authorization: Bearer sp_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": "user@example.com",
"subject": "Welcome!",
"html": "<h1>Hello World</h1>"
}'
3
Check the response
Responsejson
{
"id": "em_abc123def456",
"status": "sent"
}

Authentication

Secure your API requests with Bearer token authentication.

All API requests require authentication via a Bearer token in the Authorization header. Get your API key from the dashboard.

cURLbash
curl -X POST https://api.sendpigeon.dev/v1/emails \
-H "Authorization: Bearer sp_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"from": "...", "to": "...", "subject": "..."}'
Live Keys
Start with sp_live_ — use in production. Emails are delivered to real recipients.
Test Keys
Start with sp_test_ — use in development. Emails are logged but not sent.

Send Email

POST /v1/emails — Send a transactional email.

Request Body

ParameterTypeDescription
from*stringSender email address. Domain must be verified.
to*string | string[]Recipient email address(es).
subjectstringEmail subject. Required unless using templateId.
htmlstringHTML body content.
textstringPlain text body content.
ccstring | string[]CC recipients.
bccstring | string[]BCC recipients.
replyTostringReply-to address.
templateIdstringTemplate ID to use instead of subject/body.
variablesobjectVariables to substitute in template.

Headers

HeaderDescription
Idempotency-KeyUnique key to prevent duplicate sends. Same key returns cached response.

Example Request

cURLbash
curl -X POST https://api.sendpigeon.dev/v1/emails \
-H "Authorization: Bearer sp_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@acme.com",
"to": "user@example.com",
"subject": "Your order shipped!",
"html": "<h1>Order #1234</h1>",
"replyTo": "support@acme.com"
}'

Response

{
"id": "em_abc123def456",
"status": "sent",
"suppressed": []
}
Status values: sent, queued, failed, delivered, bounced, complained

Templates

Create reusable email templates with variable substitution.

Templates use {{variable}} syntax for dynamic content. Variables are automatically detected when you create or update a template.

Create Template

POST /v1/templates
cURLbash
curl -X POST https://api.sendpigeon.dev/v1/templates \
-H "Authorization: Bearer sp_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "welcome-email",
"subject": "Welcome to {{company}}, {{name}}!",
"html": "<h1>Hello {{name}}</h1><p>Welcome aboard.</p>"
}'
Responsejson
{
"id": "tpl_abc123",
"name": "welcome-email",
"subject": "Welcome to {{company}}, {{name}}!",
"variables": ["company", "name"]
}

Send with Template

cURLbash
curl -X POST https://api.sendpigeon.dev/v1/emails \
-H "Authorization: Bearer sp_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"from": "hello@acme.com",
"to": "user@example.com",
"templateId": "tpl_abc123",
"variables": {
"name": "John",
"company": "Acme Inc"
}
}'

Template Endpoints

MethodEndpointDescription
GET/v1/templatesList all templates
POST/v1/templatesCreate a template
GET/v1/templates/:idGet a template
PATCH/v1/templates/:idUpdate a template
DELETE/v1/templates/:idDelete a template

Error Codes

HTTP status codes and error responses.

StatusMeaningResolution
400Bad RequestCheck request body format and required fields.
401UnauthorizedVerify API key is valid and properly formatted.
402Quota ExceededUpgrade your plan or wait for quota reset.
403Domain Not VerifiedAdd DNS records for your sending domain.
404Not FoundCheck the resource ID exists.
429Rate LimitedSlow down requests. Max 100/minute.
500Server ErrorRetry with exponential backoff.

Error Response Format

{
"message": "Domain not verified. Add DNS records first."
}

Rate Limits

API requests are limited to 100 requests per minute per API key. Rate limit headers are included in every response:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200

TypeScript SDK Coming Soon

We're building a TypeScript SDK for Node.js and edge runtimes. In the meantime, use the REST API directly — it's simple and works everywhere.