Send Email
The core endpoint for sending emails via Tratto
POST /v1/emails is the core endpoint for sending emails. Send a simple transactional email, use a template, schedule for later, or attach files.
Prerequisites
Before you can send emails, you need a verified sending domain. See Domains for setup.
Minimal Send
Send a basic email with just the required fields.
cURL
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome!",
"text": "Hello, this is a test email."
}'Node.js
const response = await fetch('https://api.tratto.email/v1/emails', {
method: 'POST',
headers: {
'Authorization': 'Bearer tratto_live_...',
'Content-Type': 'application/json',
},
body: JSON.stringify({
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome!',
text: 'Hello, this is a test email.',
}),
});
const { data } = await response.json();
console.log('Email sent:', data.id);Python
import requests
response = requests.post(
'https://api.tratto.email/v1/emails',
headers={
'Authorization': 'Bearer tratto_live_...',
'Content-Type': 'application/json',
},
json={
'from': '[email protected]',
'to': '[email protected]',
'subject': 'Welcome!',
'text': 'Hello, this is a test email.',
},
)
data = response.json()['data']
print(f"Email sent: {data['id']}")Response:
{
"data": {
"id": "email_abc123xyz",
"status": "queued",
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome!",
"text": "Hello, this is a test email.",
"createdAt": "2025-06-30T12:00:00Z"
}
}Send with HTML
Include both text (fallback) and html (rendered content):
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome!",
"text": "Hello, this is a test email.",
"html": "<h1>Hello!</h1><p>This is a test email.</p>"
}'Email clients show html if supported; fall back to text otherwise.
Depending on your plan, Tratto may append a footer to the HTML version — the "Sent using Tratto" badge, mandatory on some plans (today Free) and optional on the others. See Email Footer & Branding.
Send with Markdown
Pass a markdown body instead of html and Tratto renders it server-side
into responsive, email-safe HTML (plus a plain-text part) using the
Markdown format:
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome!",
"markdown": "# Welcome, {{firstName}}!\n\nThanks for signing up.\n\n[Open your dashboard](https://app.yourdomain.com){button}",
"variables": {"firstName": "Alice"}
}'Rules and behavior:
markdownandhtmlare mutually exclusive — sending both fails with 422 ("markdown and html are mutually exclusive — provide one or the other.").- At least one of
html,text,markdown, ortemplateIdis required, and an emptymarkdownfails with 422 ("markdown must not be empty."). - The markdown is rendered once, at request time; the resulting HTML and text are stored on the email, so delivery retries never re-render.
- An explicit
textfield wins over the render-generated plain-text part. {{variables}}are substituted at delivery, after the render — same as every other send.- Raw HTML inside the markdown is escaped to literal text, and
javascript:/data:links are never linkified. See Markdown Templates for the syntax and the security model.
Send with a Template
Instead of composing HTML in code, use a template with variable substitution.
Step 1: Create a template (see Templates)
{
"name": "Welcome Email",
"html": "<h1>Hello {{firstName}}!</h1><p>Your code: {{code}}</p>"
}A template holds a name and a body. It has no subject — that belongs to each send.
Step 2: Send using the template
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Welcome, Alice!",
"templateId": "tmpl_abc123",
"variables": {
"firstName": "Alice",
"code": "SECRET123"
}
}'subject is required on every send. The subject and the template's html
(and, for a Markdown template, its pinned text part) are all rendered with
the variables you pass; a variable you don't pass becomes an empty string.
Schedule an Email
Send an email at a future time using the scheduledAt field (ISO 8601 format):
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Reminder!",
"text": "See you tomorrow.",
"scheduledAt": "2025-07-01T09:00:00Z"
}'The email will be queued and sent at the specified time (must be in the future).
Add Attachments
Include files as base64-encoded attachments:
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Invoice",
"text": "See attached invoice.",
"attachments": [
{
"filename": "invoice.pdf",
"contentType": "application/pdf",
"content": "JVBERi0xLjQKJeLj..."
}
]
}'Steps to attach a file:
- Read the file as binary
- Base64-encode the content
- Include
filename,contentType, andcontentin theattachmentsarray
JavaScript example:
const fs = require('fs');
const fileBuffer = fs.readFileSync('invoice.pdf');
const base64Content = fileBuffer.toString('base64');
const attachments = [
{
filename: 'invoice.pdf',
contentType: 'application/pdf',
content: base64Content,
},
];Tags for Organization
Add tags to organize and filter emails:
curl -X POST https://api.tratto.email/v1/emails \
-H "Authorization: Bearer tratto_live_..." \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Order Confirmation",
"text": "Your order has been confirmed.",
"tags": ["order", "transactional"]
}'Later, filter emails by tags or use them in webhooks.
Full Request Body Reference
| Field | Type | Required | Description |
|---|---|---|---|
from | string | ✓ | Sender email address (must be from a verified domain) |
to | string | ✓ | Recipient email address |
cc | string[] | Carbon copy recipients | |
bcc | string[] | Blind carbon copy recipients | |
subject | string | ✓ (if not using template) | Email subject line |
text | string | Plain text content (used as fallback) | |
html | string | HTML content (mutually exclusive with markdown) | |
markdown | string | Markdown, rendered server-side (mutually exclusive with html, max 100,000 chars) | |
templateId | string | Template ID (instead of subject/text/html) | |
variables | object | Variables to interpolate in template | |
attachments | array | Array of attachment objects | |
scheduledAt | string (ISO 8601) | Schedule email for future delivery | |
tags | string[] | Organization tags (max 10) | |
replyTo | string | Reply-to address | |
headers | object | Custom email headers (advanced) |
Response
Success (2xx):
{
"data": {
"id": "email_abc123xyz",
"status": "queued",
"from": "[email protected]",
"to": "[email protected]",
"cc": [],
"bcc": [],
"subject": "Welcome!",
"createdAt": "2025-06-30T12:00:00Z",
"scheduledAt": null
}
}Error (4xx):
{
"error": {
"code": "FORBIDDEN",
"message": "Domain 'mail.acme.com' is not verified for this tenant.",
"docs": "https://docs.tratto.email/en/domains",
"suggestion": "Add and verify the domain at https://app.tratto.email/domains before sending."
}
}Common Errors
FORBIDDEN: unverified domain
Cause: The from domain has not been verified yet. The API returns 403 with
the domain named in message.
Fix: Complete the domain verification process.
RATE_LIMITED
Cause: More than 100 requests in one second with the same API key, or more than 500 requests in one minute from the same IP.
Fix: Implement exponential backoff and retry later. See Rate Limits.
VALIDATION_ERROR
Cause: Invalid request (missing required field, malformed JSON, etc.).
Fix: Check the error message and refer to the Error Codes reference.
Next Steps
- Track email events? Set up Webhooks
- Check email status? See Email Status & Lifecycle
- Create templates? Go to Templates
- Manage domains? Read Domains
Edit this page on GitHub
Last updated on