Skip to main content

Send an email

POST /api/v1/emails/send sends one transactional email — either raw HTML/text or a saved template — from a verified FilesHub domain. It is queued by default: the call returns immediately with a job id you can poll, and delivery follows within about a minute with automatic retries.

POST /api/v1/emails/send

Permission: email · Content-Type: application/json

Request

Headers

HeaderRequiredNotes
X-API-KeyYesA key with the email permission (can_send_emails).
Content-TypeYesapplication/json.
X-App-IdIf the key is app-restrictedYour app package / bundle id. See API key restrictions.

Body — raw content

Send one recipient per call (loop for more). Provide at least one of body_html / body_text.

FieldRequiredTypeDescription
toYesemailThe recipient address.
to_nameNostring (≤255)Recipient display name.
subjectYesstring (≤500)Subject line.
body_htmlOne of html/textstringHTML body (max ~500 KB, configurable per deployment).
body_textOne of html/textstringPlain-text body (max ~500 KB).
from_nameNostring (≤255)Display name on the From line. The address is the sending domain's mailbox.
ccNoarray (≤10) of emailsCarbon-copy recipients.
bccNoarray (≤10) of emailsBlind-copy recipients.
reply_toNoemailReply-to address.
attachmentsNoarray (≤5) of ULIDsPre-uploaded FilesHub object ids (upload with POST /objects first). Each entry is a 26-char ULID.
domainNostringWhich verified sending domain to use — aoneahsan.com (default), zaions.com, or trizlink.com. Omitted → the admin-set default with automatic fallback.
queueNobooleantrue (default) queues the send; false sends synchronously (use for OTP / latency-sensitive mail).
scheduled_atNoISO-8601 (future)Send once at a future time instead of now.

Body — template

To send a saved template, send template instead of subject/body_*:

FieldRequiredTypeDescription
templateYesstringThe template slug.
variablesNoobjectValues substituted into the template's placeholders.

to, cc, bcc, reply_to, from_name, domain, queue, attachments, and scheduled_at apply to template sends too.

Examples

curl — raw HTML, queued
curl -X POST https://fileshub.zaions.com/api/v1/emails/send \
-H "X-API-Key: fh_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"to_name": "Sam",
"subject": "Welcome",
"body_html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
"from_name": "Acme",
"domain": "zaions.com"
}'
curl — synchronous OTP
curl -X POST https://fileshub.zaions.com/api/v1/emails/send \
-H "X-API-Key: fh_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"subject": "Your code",
"body_text": "Your code is 123456",
"queue": false
}'
fetch — template
const res = await fetch('https://fileshub.zaions.com/api/v1/emails/send', {
method: 'POST',
headers: { 'X-API-Key': 'fh_live_xxx', 'Content-Type': 'application/json' },
body: JSON.stringify({
to: 'user@example.com',
template: 'welcome',
variables: { name: 'Sam', plan: 'Pro' },
}),
});
const { data } = await res.json();

Response

202 Accepted — queued (default)

{
"success": true,
"data": {
"id": "01JEMAILLOGXXXXXXXXXXXXXXX",
"status": "queued",
"from_email": "noreply@zaions.com",
"domain": "zaions.com",
"job_id": "01JJOBXXXXXXXXXXXXXXXXXXXX",
"status_url": "https://fileshub.zaions.com/api/v1/jobs/01JJOBXXXXXXXXXXXXXXXXXXXX",
"scheduled_at": null,
"sent_at": null
}
}

Poll status_url — see Job status — until the job is completed or failed.

201 Created — synchronous (queue: false) or scheduled

Same shape; status is the delivered status (e.g. sent) and sent_at is set. For a scheduled_at send, status_url points at the email log and scheduled_at is echoed back.

Errors

StatusWhen
401No / invalid X-API-Key.
403Key lacks the email permission, or origin/app not allowed.
422Validation failed — missing recipient/body, unknown/inactive domain, bad attachment id.
429The sending account's daily quota is exhausted. Retry after the reset (or the key's email_daily_limit).
503The requested domain is temporarily without sending capacity. Retry with backoff.

Notes

  • One recipient per call. Use cc/bcc for extra visible/hidden copies; loop for separate sends.
  • Domains are verified mailboxes. The From address is the domain's own mailbox (e.g. noreply@zaions.com); from_name still rides along.
  • Attachments are FilesHub objects. Upload the file first, then pass its ULID — there is no direct MIME attachment.
  • Quotas are per sending account. Queue and retry on 429/503; queued sends already retry automatically (three attempts with backoff).
  • Audit. Send an optional X-App-Id header and FilesHub records the caller's app id, ip, and user-agent on the email log for debugging.