Skip to main content

Send Email

POST /v1/send

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.

Request Body

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.

Response

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
}

Examples

curl

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"]
}'

Node.js SDK

import { Optail } from '@optail/node';

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'],
});

Template-based send

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"
}'

POST /v1/emails/batch

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

Request Body

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

Response

Status: 200 OK

{
"results": [
{ "messageId": "msg-1-uuid", "status": "queued" },
{ "messageId": "msg-2-uuid", "status": "queued" },
{ "error": "Validation failed: 'to' is required" }
]
}

Node.js SDK

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}`);
}
}

How Routing Works

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.