Sending Emails
Relay provides three endpoints for sending emails: single send, template send, and batch send. All require an API key with the send scope.
Authentication
Include your API key in the X-Relay-Key header:
X-Relay-Key: relay_key_abc123...
Single Send
POST /api/send — Send one email to one or more recipients.
Request
{
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Ticket #1234 Updated",
"html": "<h1>Ticket Updated</h1><p>Your ticket has been assigned.</p>",
"text": "Ticket Updated\nYour ticket has been assigned.",
"reply_to": "[email protected]",
"cc": ["[email protected]"],
"bcc": ["[email protected]"],
"tags": {
"product": "psa",
"ticket_id": "1234"
},
"attachments": [
{
"filename": "report.pdf",
"content": "base64-encoded-content",
"content_type": "application/pdf"
}
]
}
Fields
| Field | Required | Description |
|---|---|---|
from | Yes | Sender address. Must be on a verified domain. |
to | Yes | Array of recipient email addresses |
subject | Yes | Email subject line |
html | Yes* | HTML body (*at least one of html or text required) |
text | No | Plain text fallback body |
reply_to | No | Reply-to address |
cc | No | Array of CC addresses |
bcc | No | Array of BCC addresses |
tags | No | Key-value metadata for tracking and analytics |
attachments | No | Array of attachments (max 10, base64-encoded) |
Response
{
"message_id": "msg_abc123...",
"status": "sent",
"ses_message_id": "0100018e..."
}
Validation
Before sending, Relay checks:
- Domain verified — The
fromaddress domain must be verified in your account - Suppressions — All
to,cc, andbccaddresses are checked against the suppression list. Suppressed addresses are skipped. - Rate limits — Your tenant's rate limits are checked across all four windows
If the domain is not verified, the request returns 400. If all recipients are suppressed, it returns 400. If rate limited, it returns 429 with a Retry-After header.
Template Send
POST /api/send-template — Send using a saved template with variable substitution.
Request
{
"from": "[email protected]",
"to": ["[email protected]"],
"template_id": "tmpl_welcome_123",
"variables": {
"client_name": "Acme Corp",
"technician": "Sarah",
"ticket_url": "https://portal.yourmsp.com/tickets/1234"
},
"reply_to": "[email protected]",
"tags": {
"product": "portal",
"type": "welcome"
}
}
| Field | Required | Description |
|---|---|---|
from | Yes | Sender address on a verified domain |
to | Yes | Array of recipient addresses |
template_id | Yes | ID of a saved template |
variables | No | Key-value pairs replacing {{variable}} placeholders in the template |
reply_to | No | Reply-to address |
tags | No | Tracking metadata |
The template's subject, HTML, and text fields all support {{variable}} substitution. Any unreplaced variables remain as-is in the output.
Batch Send
POST /api/send-batch — Send up to 50 emails in a single API call.
Requires both send and send-batch scopes on your API key.
Request
{
"emails": [
{
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Invoice Ready",
"html": "<p>Your invoice is ready.</p>"
},
{
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Invoice Ready",
"html": "<p>Your invoice is ready.</p>"
}
]
}
Each item in the emails array follows the same format as a single send request.
Response
{
"results": [
{ "message_id": "msg_abc...", "status": "sent", "ses_message_id": "010001..." },
{ "message_id": "msg_def...", "status": "sent", "ses_message_id": "010002..." }
],
"total": 2,
"sent": 2,
"failed": 0
}
Each email is validated independently. If one email fails (bad recipient, suppressed, etc.), the others still send. The response includes per-message results.
Limits
- Maximum 50 emails per batch request
- Each email counts individually against rate limits
- Batch requests that exceed 50 emails return
400
Error Responses
| Status | Meaning |
|---|---|
200 | Email sent successfully |
400 | Validation error — unverified domain, missing fields, all recipients suppressed, batch too large |
401 | Missing or invalid API key |
403 | API key lacks required scope |
429 | Rate limit exceeded — check Retry-After header |
500 | Internal error — retry with exponential backoff |
Tags and Tracking
Tags are key-value pairs attached to each email. They flow through the entire lifecycle (send → delivery → bounce → analytics) and enable filtering in logs and segmentation in analytics.
Common tag patterns:
{
"product": "psa",
"type": "ticket_notification",
"org_id": "org_123",
"ticket_id": "1234"
}
Tags appear in email logs and the analytics "By Product" breakdown.
SDK Usage
The TypeScript SDK wraps all three endpoints:
import { RelayClient } from '@theonefamily/relay-sdk';
const relay = new RelayClient({
apiKey: process.env.RELAY_API_KEY,
});
// Single send
await relay.send({ from, to, subject, html });
// Template send
await relay.sendTemplate({ from, to, template_id, variables });
// Batch send
await relay.sendBatch({ emails: [{ from, to, subject, html }, ...] });