Skip to content

REST API Reference

https://relaypost.dev/api/v1

All requests require an API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

API keys are created in the RelayPost dashboard under Settings → API Keys. See API Keys for details.

All successful responses are wrapped in a data envelope:

{
"data": { ... }
}

Paginated responses include a pagination object:

{
"data": [...],
"pagination": {
"page": 1,
"limit": 20,
"total_count": 150,
"total_pages": 8
}
}

Error responses use a consistent error envelope:

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{ "field": "to", "message": "At least one recipient is required" }
]
}
}

See Error Codes for the full list.

Every response includes rate limit headers. See Rate Limits for details.

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRequests remaining in the current window
Retry-AfterSeconds to wait before retrying (only on 429 responses)

POST /api/v1/emails/send

Terminal window
curl -X POST https://relaypost.dev/api/v1/emails/send \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"from": { "email": "[email protected]", "name": "Your App" },
"to": [{ "email": "[email protected]" }],
"subject": "Your receipt",
"html": "<h1>Thanks for your purchase!</h1>"
}'
FieldTypeRequiredDescription
fromobjectYesSender — { "email": "...", "name": "..." }
toarrayYesRecipients — [{ "email": "...", "name": "..." }]
ccarrayNoCC recipients
bccarrayNoBCC recipients
subjectstringYesEmail subject line
htmlstringNoHTML body
textstringNoPlain text body
template_idstringNoUse a saved template instead of inline content
template_dataobjectNoKey-value pairs for template variables
headersobjectNoCustom email headers
prioritystringNohigh, normal (default), or low
scheduled_atstringNoISO 8601 datetime for scheduled delivery

You must provide either html, text, or template_id.

{
"data": {
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "queued",
"queued_at": "2025-01-15T10:30:00.000Z"
}
}
StatusCodeDescription
400VALIDATION_ERRORMissing or invalid fields
400DOMAIN_NOT_VERIFIEDFrom address uses an unverified domain
400RECIPIENTS_SUPPRESSEDOne or more recipients are on the suppression list
429LIMIT_EXCEEDEDOrganization email sending limit exceeded

GET /api/v1/emails

ParameterTypeDefaultDescription
statusstringFilter by status: queued, delivered, bounced, failed, opened
from_datestringISO 8601 start date
to_datestringISO 8601 end date
pageinteger1Page number
limitinteger20Results per page (max 100)
{
"data": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"from_address": "[email protected]",
"to_addresses": ["[email protected]"],
"subject": "Your receipt",
"status": "delivered",
"priority": "normal",
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:05.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total_count": 42,
"total_pages": 3
}
}

GET /api/v1/emails/:id

Retrieve a specific email by its ID or message ID, including delivery events.

{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"message_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"from_address": "[email protected]",
"to_addresses": ["[email protected]"],
"subject": "Your receipt",
"status": "delivered",
"priority": "normal",
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:05.000Z",
"scheduled_at": null,
"events": [
{
"id": "evt_001",
"type": "delivered",
"recipient": "[email protected]",
"smtp_code": 250,
"smtp_message": "OK",
"created_at": "2025-01-15T10:30:05.000Z"
}
]
}
}
StatusCodeDescription
404NOT_FOUNDEmail not found or belongs to another organization

POST /api/v1/domains

FieldTypeRequiredDescription
domainstringYesThe domain name (e.g. example.com)
{
"data": {
"id": "dom_abc123",
"domain": "example.com",
"is_verified": false,
"spf_verified": false,
"dkim_verified": false,
"dkim_selector": "relaypost",
"dkim_public_key": "MIGfMA0GCSqGSIb3DQEBAQUAA4...",
"dns_records": [
{
"type": "TXT",
"name": "relaypost._domainkey.example.com",
"value": "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4..."
},
{
"type": "TXT",
"name": "example.com",
"value": "v=spf1 include:relaypost.dev ~all"
}
],
"created_at": "2025-01-15T10:30:00.000Z"
}
}

GET /api/v1/domains

{
"data": [
{
"id": "dom_abc123",
"domain": "example.com",
"is_verified": true,
"spf_verified": true,
"dkim_verified": true,
"dkim_selector": "relaypost",
"dkim_public_key": "MIGfMA0GCSqGSIb3DQEBAQUAA4...",
"verified_at": "2025-01-15T11:00:00.000Z",
"created_at": "2025-01-15T10:30:00.000Z"
}
]
}

GET /api/v1/domains/:id/verify

Performs DNS verification and returns the updated domain status.

{
"data": {
"id": "dom_abc123",
"domain": "example.com",
"is_verified": true,
"spf_verified": true,
"dkim_verified": true,
"dkim_selector": "relaypost",
"dkim_public_key": "MIGfMA0GCSqGSIb3DQEBAQUAA4...",
"verified_at": "2025-01-15T11:00:00.000Z",
"created_at": "2025-01-15T10:30:00.000Z"
}
}
StatusCodeDescription
404NOT_FOUNDDomain not found or belongs to another organization

GET /api/v1/suppressions

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Results per page (max 100)
{
"data": [
{
"id": "sup_abc123",
"email": "[email protected]",
"reason": "hard_bounce",
"source": "automatic",
"created_at": "2025-01-15T10:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total_count": 15,
"total_pages": 1
}
}

POST /api/v1/suppressions

FieldTypeRequiredDescription
emailstringYesEmail address to suppress
reasonstringYesOne of: hard_bounce, soft_bounce, complaint, unsubscribe, manual
{
"data": {
"id": "sup_def456",
"email": "[email protected]",
"reason": "hard_bounce",
"source": "manual",
"created_at": "2025-01-15T10:30:00.000Z"
}
}
StatusCodeDescription
400VALIDATION_ERRORInvalid email or reason

DELETE /api/v1/suppressions/:id

{
"data": {
"id": "sup_abc123",
"deleted": true
}
}
StatusCodeDescription
404NOT_FOUNDSuppression not found or belongs to another organization

GET /api/v1/templates

ParameterTypeDefaultDescription
pageinteger1Page number
limitinteger20Results per page (max 100)
{
"data": [
{
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "...",
"text_body": "...",
"variables": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total_count": 5,
"total_pages": 1
}
}

POST /api/v1/templates

FieldTypeRequiredDescription
namestringYesTemplate name
subjectstringYesSubject line (supports {{variables}})
htmlstringNoHTML body (supports {{variables}})
textstringNoPlain text body (supports {{variables}})
{
"data": {
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "<h1>Thanks, {{customerName}}!</h1>",
"text_body": "Thanks, {{customerName}}!",
"variables": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T10:30:00.000Z"
}
}

PUT /api/v1/templates/:id

Only include the fields you want to change.

FieldTypeRequiredDescription
namestringNoUpdated template name
subjectstringNoUpdated subject line
htmlstringNoUpdated HTML body
textstringNoUpdated plain text body

At least one field must be provided.

{
"data": {
"id": "tmpl_abc123",
"name": "Order Confirmation",
"subject": "Your order #{{orderNumber}}",
"html_body": "<h1>Updated layout</h1>",
"text_body": "Thanks, {{customerName}}!",
"variables": null,
"is_active": true,
"created_at": "2025-01-15T10:30:00.000Z",
"updated_at": "2025-01-15T11:00:00.000Z"
}
}
StatusCodeDescription
400VALIDATION_ERRORNo update fields provided or invalid values
404NOT_FOUNDTemplate not found or belongs to another organization

DELETE /api/v1/templates/:id

{
"data": {
"id": "tmpl_abc123",
"deleted": true
}
}
StatusCodeDescription
404NOT_FOUNDTemplate not found or belongs to another organization