Skip to content

Send Email

Send an email through Optail. The API validates the request, checks suppressions, resolves the provider via routing rules, and queues the email for delivery. Returns 202 Accepted immediately.

FieldTypeRequiredDescription
tostring | string[]YesRecipient email(s). Max 1,000 per request.
fromstringYesSender email address. Domain must be verified on the resolved provider.
subjectstringYesEmail subject line (max 998 characters).
htmlstringConditionalHTML body. Required if templateId and text are not provided.
textstringConditionalPlain text body. Required if templateId and html are not provided.
templateIdstringConditionalUUID of a template to render. Required if html and text are not provided.
variablesobjectNoKey-value pairs for Handlebars template variables.
replyTostringNoReply-to email address.
tagsstring[]NoUp to 10 tags (max 100 chars each). Used for routing and analytics.
metadataobjectNoArbitrary metadata stored with the message.
unsubscribeGroupIdstringNoUUID of the unsubscribe group. Adds an unsubscribe link and checks group-specific suppressions.

At least one of templateId, html, or text must be provided.

Status: 202 Accepted

{
"messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"providerId": "p1q2r3s4-t5u6-7890-vwxy-z12345678901",
"providerType": "SENDGRID",
"status": "queued",
"domainVerified": true
}

If all recipients are suppressed:

{
"messageId": null,
"providerId": null,
"status": "suppressed",
"suppressedCount": 1
}
Terminal window
curl -X POST https://api.optail.io/v1/send \
-H "Authorization: Bearer ms_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Welcome!",
"html": "<h1>Hello World</h1>",
"tags": ["welcome"]
}'
import { Optail } from 'optail';
const optail = new Optail({
apiKey: process.env.OPTAIL_API_KEY,
});
const result = await optail.send({
to: 'user@example.com',
from: 'hello@yourdomain.com',
subject: 'Welcome!',
html: '<h1>Hello World</h1>',
tags: ['welcome'],
});
Terminal window
curl -X POST https://api.optail.io/v1/send \
-H "Authorization: Bearer ms_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"from": "hello@yourdomain.com",
"subject": "Welcome, {{name}}!",
"templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"variables": {
"name": "Jane",
"activationUrl": "https://app.example.com/activate?token=abc"
},
"unsubscribeGroupId": "g1h2i3j4-k5l6-7890-mnop-q12345678901"
}'

Send multiple emails in a single request. Each message is independently validated and queued.

FieldTypeRequiredDescription
messagesSendEmailParams[]YesArray of email objects (same schema as single send).

Status: 200 OK

{
"results": [
{ "messageId": "msg-1-uuid", "status": "queued" },
{ "messageId": "msg-2-uuid", "status": "queued" },
{ "error": "Validation failed: 'to' is required" }
]
}
const result = await optail.sendBatch({
messages: [
{
to: 'alice@example.com',
from: 'hello@yourdomain.com',
subject: 'Hello Alice',
html: '<p>Hi Alice!</p>',
},
{
to: 'bob@example.com',
from: 'hello@yourdomain.com',
subject: 'Hello Bob',
html: '<p>Hi Bob!</p>',
},
],
});
for (const item of result.results) {
if ('messageId' in item) {
console.log(`Sent: ${item.messageId}`);
} else {
console.error(`Failed: ${item.error}`);
}
}

When you send an email, Optail resolves which provider to use:

  1. Match routing rules by domain and tags, ordered by priority (highest first)
  2. The first matching rule’s provider is selected
  3. If no rule matches, the first active provider account is used as a fallback

Configure routing rules in the dashboard or via the Providers API.