Docs/API reference/Send email

Send email

Render a published template version with your variables and queue it for delivery through your workspace's ESP.

v1 · beta
http
POST https://www.ewyn.ai/api/v1/send

Request body

FieldTypeRequiredNotes
tostringyesRecipient email address
subjectstringyesSubject line; may contain {{variable}} placeholders
templateVersionIduuidone ofPublished template version ID. templateId is accepted as a legacy alias — exactly one of the two is required
typestringnotransactional or marketing. Omitted: the template's email type when set in the dashboard, else transactional. See Subscriptions and unsubscribe
topicstringnoSubscription topic slug for a marketing send (for example newsletter). Omitted: the template's default topic, else general marketing. Not allowed with type: "transactional"
preheaderstringnoPreview text after the subject in most clients; supports {{variable}} placeholders
variablesobjectnoString map substituted into {{ }} tokens in the template, subject, and preheader. Names starting with @ are reserved for system variables such as {{@unsubscribeUrl}} and are rejected with 400
metadataobjectnoAttached to the message for your own correlation
idempotencyKeystringnoDedupes repeat requests for 24 hours
fromstringnoOverride the workspace default sender address
fromNamestringnoOverride the sender display name (max 255 chars)
replyTostringnoOverride the workspace default reply-to
attachmentsarraynoUp to 10 files to attach — see Attachments below
Transactional sends ignore unsubscribes and spam complaints by design.

Only mark email transactional when the recipient expects it as part of a service they use — receipts, confirmations, password resets. A transactional send is stopped only by a hard bounce or a manual suppression; a marketing send is also stopped by a spam complaint and by an opt-out from its topic or from all marketing.

Attachments

Each entry attaches one file. Provide exactly one of content (base64) or url (an https URL Ewyn downloads at send time, so your file server only needs to be reachable while the request is in flight).

FieldTypeRequiredNotes
filenamestringyesName shown to the recipient (max 255 chars)
contentstringone ofStandard base64 of the file bytes, no data: prefix
urlstringone ofhttps URL; private and internal addresses are refused
contentTypestringnoMIME type; inferred from the response or extension when omitted
contentIdstringnoEmbeds the file inline; reference it in the template as <img src="cid:logo">
Limits
  • 10 attachments per message.
  • Inline content: 2.5 MiB total (decoded). The whole request body must stay under 4 MB — use url for larger files.
  • 7 MiB total across all attachments, inline and fetched.
  • Executables, scripts, HTML and SVG files are rejected with ATTACHMENT_TYPE_BLOCKED.
  • Attachment bytes are retained for 6 months, then deleted.
  • Idempotent replays return the original message; attachments on a replay are ignored (attachmentsIgnored in the response). Sends to suppressed recipients don't store attachments (attachmentsDropped in the 422) and can't be resent from the dashboard.

Example

bash
curl -X POST https://www.ewyn.ai/api/v1/send \
  -H "Authorization: Bearer $EWYN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "maya@example.com",
    "subject": "Welcome, {{firstName}}!",
    "templateVersionId": "4f6f3a0e-9b1c-4f7a-8a3e-version-uuid",
    "type": "transactional",
    "variables": { "firstName": "Maya" },
    "idempotencyKey": "signup-usr_8h2k4",
    "attachments": [
      { "filename": "welcome.pdf", "content": "JVBERi0xLjQK...", "contentType": "application/pdf" },
      { "filename": "terms.pdf", "url": "https://files.yourdomain.com/terms.pdf" }
    ]
  }'

Response

json
{
  "messageId": "msg_...",
  "status": "queued",
  "queuedAt": "2026-06-12T09:30:00.000Z",
  "type": "transactional"
}

queued means Ewyn accepted the message and handed it to the delivery queue. Delivery, open, click, and bounce events appear on the contact's journey timeline in the dashboard as your ESP reports them back.

type is the email type as the server resolved it — your type, else the template's. A marketing send also echoes topic when one applied (yours, or the template's default), so an integration can see what the server decided.

Idempotent replays return 200.

A repeated idempotencyKey within 24 hours returns the original messageId without sending again. If the original attempt terminally failed, the replay re-queues it instead. A replay of a send that was blocked (422) is judged again: once the recipient is no longer blocked — the opt-out or suppression was removed — the replay queues it and answers with status: "queued".

Blocked recipients

When the recipient must not receive this send, the message is recorded with a Suppressed status and the request fails with 422 RECIPIENT_SUPPRESSED. details.kind tells a delivery block from a marketing opt-out:

json
{
  "code": "RECIPIENT_SUPPRESSED",
  "error": "maya@example.com unsubscribed from topic \"newsletter\". Marketing email is not sent to them. ...",
  "reason": "OPT_OUT_TOPIC",
  "details": { "kind": "opt_out", "reason": "OPT_OUT_TOPIC", "topic": "newsletter" },
  "type": "marketing",
  "topic": "newsletter",
  "messageId": "msg_..."
}
details.kinddetails.reasonBlocks
suppressionHARD_BOUNCEevery send
suppressionMANUALevery send
suppressionCOMPLAINTmarketing sends
opt_outOPT_OUT_TOPICmarketing sends under that topic; details.topic names it
opt_outOPT_OUT_ALLevery marketing send

An opt-out never blocks a transactional send. If a service email is refused with kind: "opt_out", send it with type: "transactional".

Errors

StatusMeaning
400Validation failed — missing subject, invalid email, no template version ID, topic combined with type: "transactional", or a variable name starting with @
400ATTACHMENT_INVALID (bad base64, non-https url, blocked host, duplicate contentId) or ATTACHMENT_TYPE_BLOCKED. attachment.index names the offending file.
401Missing or invalid API key
404Template version not found in this workspace
410ATTACHMENTS_EXPIRED — an idempotent replay of a failed send whose attachments were deleted after the retention period; send again with a new key
413ATTACHMENT_TOO_LARGE (inline or total attachment limit) or REQUEST_TOO_LARGE (body over 4 MB)
422RECIPIENT_SUPPRESSED — the recipient is blocked for this type of email; see Blocked recipients
422TOPIC_NOT_FOUND — topic names no active topic in this workspace (unknown or archived)
422ATTACHMENT_FETCH_FAILED — a url attachment returned an error, timed out, or was empty
429Rate limited, QUOTA_EXCEEDED, or ATTACHMENT_QUOTA_EXCEEDED — see Errors & rate limits
Was this page helpful?