Emails
Send transactional email through your verified domains.
The Emails resource sends one-to-one transactional email (receipts, notifications, password resets) from any verified domain connection. Emails are queued asynchronously via AWS SQS and return 202 Accepted immediately.
Send an emailPOST/v1/emails
Permission: write.
| Field | Type | Required | Description |
|---|---|---|---|
| from | string | Yes | Sender email. The domain must be configured and verified for outbound email in Connections. |
| from_name | string | No | Sender display name. |
| to | string[] | Yes | Array of recipient email addresses. The maximum per request depends on your plan (see Limits below). |
| subject | string | Yes | Email subject line. |
| html | string | Conditional | HTML email body. Provide html OR text (or both). |
| text | string | Conditional | Plain text email body. |
| cc | string[] | No | CC recipients. |
| bcc | string[] | No | BCC recipients. |
| reply_to | string | No | Reply-to address. Applied to immediate sends only; scheduled emails (send_at) are currently sent without a Reply-To header, so replies go to the from address. |
| metadata | object | No | Arbitrary JSON metadata stored with the email. |
| track_opens | boolean | No | Enable open tracking pixel (requires html). Default: false. |
| track_clicks | boolean | No | Rewrite links for click tracking (requires html). Default: false. |
| type_sent | string | No | transactional (default) or broadcast. Determines the SQS queue. |
| files | array | No | Attachments: [{ fileName, fileData (base64), contentType }]. Max 5 files per email, 10 MB per file. |
| send_at | string | No | ISO 8601 date to schedule the email. Must be in the future and at most 30 days ahead. If omitted, the email sends immediately. |
Returns 202 with { id, status: "queued", queued_at } for immediate emails, or { id, status: "scheduled", scheduled_at } for scheduled emails. Scheduled emails are processed by a cron job every minute.
At send time, scheduled emails are checked again: if the account is suspended, the send limits are reached or the primary recipient is suppressed, the email is marked blocked; if the sending domain is no longer verified, it is marked failed.
Attachments
Each entry in files needs fileName, fileData (base64-encoded content) and contentType. Up to 5 files per email and 10 MB per file (more files, or a larger file, returns 400). Attachments are stored in your account storage: if the upload would exceed your plan storage quota, the request returns 429 with the limit-exceeded body described below.
Limits
Every send is checked against your plan before it is accepted:
| Plan | Max recipients in to per request |
|---|---|
| Free | 10 |
| Starter | 50 |
| Pro | 100 |
| Enterprise | 200 |
The monthly email quota of your plan, plus daily and hourly sending limits, are also enforced. When a recipient count, send limit or storage quota is exceeded, the API returns 429 with this body:
If your account is suspended, sends return 403 with { "error": "Account suspended. Please contact support." }.
Suppressed recipients
Before sending, every address in to is checked against its contact record. A recipient is rejected when the contact has 3 or more recorded bounces (metadata.bounce_count) or is marked undeliverable (metadata.email_deliverable set to "false"). The whole request is rejected with HTTP 422 -- no email is sent to any recipient:
Only the to addresses are checked; cc and bcc addresses are not.
List scheduled emailsGET/v1/emails/scheduled
Permission: read. Returns emails with status "scheduled", ordered by scheduled_at ascending. Query params: limit (default 50, max 100) and offset.
Reschedule an emailPATCH/v1/emails/{id}/schedule
Permission: write. Change the send_at date of a scheduled email. The new date must be in the future and at most 30 days ahead. Only works on emails with status "scheduled" (other statuses return 400). Returns { id, status: "scheduled", scheduled_at, message }.
Cancel a scheduled emailDELETE/v1/emails/{id}/schedule
Permission: write. Cancels a scheduled email before it is sent. Only works on emails with status "scheduled" (other statuses return 400).
The response is { id, status: "cancelled", message: "Scheduled email cancelled successfully" }. Note that the stored email is marked as failed with error_message "Cancelled by user": a later GET /v1/emails/{id} returns status "failed", not "cancelled".
Get email statusGET/v1/emails/{id}
Permission: read. Returns the current delivery state of a single email, including bounce details when applicable. Useful for polling after a send. The response contains id, status, from, to (primary recipient), cc, subject, type_sent, created_at and the fields below.
| Field | Type | Description |
|---|---|---|
| status | string | scheduled, queued, processing, sent, delivered, bounced, failed or blocked. Cancelled scheduled emails show failed. |
| queued_at | string | null | When the email entered the send queue. |
| scheduled_at | string | null | When the email is scheduled to send (scheduled emails). |
| delivered_at | string | null | Delivery confirmation timestamp. |
| bounced_at | string | null | Bounce timestamp, if any. |
| error_message | string | null | Failure reason when status is failed (for example "Cancelled by user"). |
| bounce | object | null | { type, sub_type, recipients } when the email bounced. |
Send from a templatePOST/v1/emails/template
Permission: write. Sends an email using a saved template with variable substitution. Template sends are always immediate and transactional: this endpoint does not accept send_at, files, text or type_sent. The same plan limits, suppression checks and 403/422/429 responses as POST /v1/emails apply.
| Field | Type | Required | Description |
|---|---|---|---|
| template_id | string | Yes | Template ID (e.g. etpl_xxxxxxxx). Unknown IDs return 404. |
| to | string[] | Yes | Array of recipient email addresses. |
| variables | object | No | Custom variable values as strings: { "order_id": "A-1001" }. See precedence below. |
| from | string | No | Override the template sender email. Required if the template has none. |
| from_name | string | No | Override the template sender name. |
| subject | string | No | Override the template subject. Required if the template has none. |
| reply_to | string | No | Override the template reply-to address. |
| cc | string[] | No | CC recipients. |
| bcc | string[] | No | BCC recipients. |
| track_opens | boolean | No | Default: false. |
| track_clicks | boolean | No | Default: false. |
| metadata | object | No | Stored with the email. TickOS adds template_id and template_name to it. |
Returns 202 with { id, status: "queued", template_id, template_name, queued_at }.
Template variables
Variables use {{key}} syntax with no spaces inside the braces ({{ order_id }} is not replaced). Values are HTML-escaped and replaced in both the HTML body and the subject. Variables are resolved in this order:
- System variables: name, first_name, email and phone. They are always resolved from the contact record of the first address in to (empty string if unknown; email falls back to the recipient address).
- Custom variables from the variables object in the request.
- Default values defined in the template.
Email statisticsGET/v1/emails/stats
Permission: read. Returns delivery, tracking and health metrics.
| Query param | Description |
|---|---|
| start_date | ISO 8601 date. Only emails created on or after this date are counted. Default: all time. |
| include_timeline | Set to true to add a timeline object: { days: [{ date, received, sent, broadcast }] }, a daily series (UTC) of inbound, transactional and campaign emails. |
The response contains total, sent, received, byStatus { delivered, sent, pending, failed }, tracking { emailsWithTracking, uniqueOpens, uniqueClicks, openRate, clickRate } and health (last 7 days: totalSent7d, totalDelivered7d, totalBounced7d, totalComplained7d, bounceRate, complaintRate, deliveryRate, plus hard/soft bounce breakdown fields that are null when not available).