Emails
Send email, look up messages and their content, and schedule, cancel, retry or forward them.
Send an email
Sends an email from a verified sending domain. Every recipient gets a separate copy with its own email ID, and each recipient costs one credit.
/emailsWorks with sending and full API keys. Sends count against the workspace’s sending limits, and a successful response means the email is accepted and queued, not yet delivered. Track delivery with webhooks or Retrieve an email. Unverified workspaces can only send to their members’ account emails.
Headers
Idempotency-KeystringA unique key, up to 256 letters, digits, - and _. A retry with the same key within 24 hours returns the first response instead of sending again. See Idempotency.
Body parameters
fromstringrequiredThe sender, as hello@acme.com or Acme <hello@acme.com>. The address must be on a verified sending domain of the workspace, and on the key’s domain if the key is restricted to one.
tostring | string[]requiredRecipients, as an array or a comma-separated string. Each entry can be ada@example.com or Ada Lovelace <ada@example.com>. Up to 50.
ccstring | string[]bccstring | string[]reply_tostring | string[]subjectstringtemplate provides one.htmlstringhtml, text or both, unless template provides content.textstringhtml and text, recipients get a multipart message.templatestringA template to send. Pass a template ID (tem_…) to use that exact version, or an alias to use its published version. subject, html and text in the request override the template’s. See Templates.
variablesobjectValues for Temple placeholders such as {{first_name}}, rendered in the subject, HTML and text. Works with templates and with inline content.
attachmentsobject[]headersobjectExtra MIME headers as name–value pairs, for example {"List-Unsubscribe": "<https://acme.com/unsubscribe>"}. Emailit sets Message-ID itself.
metaobjectYour own key–value data, for example {"order_id": "1042"}. Values must be strings. Stored with the email and included in reads and webhook payloads.
scheduled_atstringWhen to send, as an ISO 8601 date-time such as 2026-10-02T09:00:00Z, or in English such as tomorrow at 9am. Include a time zone in ISO 8601 values. A time in the past, or a value that can’t be parsed (including a Unix timestamp), sends the email right away. Scheduled emails have the status scheduled until they’re sent.
trackingboolean | objectTurns open and click tracking on or off for this email: true, false, or {"loads": true, "clicks": false}. Defaults to the sending domain’s settings. Tracking only works when the domain’s tracking CNAME is verified; otherwise it’s off and the response shows false.
Attachment object
filenamestringrequiredcontentstringcontent or url, not both.urlstringA public http or https URL to download the file from. Emailit fetches it when you send: the download must finish within 30 seconds, be at most 25 MB, and must not redirect.
content_typestringapplication/pdf. Required with content. With url, defaults to the type the server returns.content_idstringMakes the attachment inline. Reference it in the HTML as <img src="cid:logo"> when content_id is logo.
encodingstringdefault: base64content, such as base64 or hex.The whole message, including attachments, can be up to 40 MB. These file types are allowed:
| Category | Extensions |
|---|---|
| Text | .txt, .csv, .log, .css, .ics, .xml |
| Images | .jpg, .jpe, .jpeg, .gif, .png, .bmp, .psd, .tif, .tiff, .svg, .indd, .ai, .eps |
| Documents | .doc, .docx, .rtf, .odt, .ott, .pdf, .pub, .pages, .mobi, .epub |
| Audio | .mp3, .m4a, .m4v, .wma, .ogg, .flac, .wav, .aif, .aifc, .aiff |
| Video | .mp4, .mov, .avi, .mkv, .mpeg, .mpg, .wmv |
| Spreadsheets | .xls, .xlsx, .ods, .numbers |
| Presentations | .odp, .ppt, .pptx, .pps, .key |
| Archives | .zip, .vcf |
.eml |
|
| Cryptographic | .p7c, .p7m, .p7s, .pgp, .asc, .sig |
Returns
Returns 200 with the email object of the first recipient. When the message has more than one recipient across to, cc and bcc, ids maps every recipient to the ID of their copy. Each copy fires an email.accepted or email.scheduled event.
objectstringemail.idstringidsobjecttokenstringmessage_idstringMessage-ID header, such as <token@acme.com>.fromstringtostring[]to addresses, without display names or duplicates.ccstring[]cc recipients. Only present when you sent some.bccstring[]bcc recipients. Only present when you sent some.subjectstringstatusstringaccepted, or scheduled for a future scheduled_at.scheduled_atstring | nullnull.created_atstringtrackingobjectloads and clicks booleans.curl -X POST https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme <hello@acme.com>",
"to": "ada@example.com",
"template": "welcome",
"variables": {
"first_name": "Ada",
"activation_url": "https://acme.com/activate?token=8f2c1e"
}
}'const email = await emailit.emails.send({
from: 'Acme <hello@acme.com>',
to: 'ada@example.com',
template: 'welcome',
variables: {
first_name: 'Ada',
activation_url: 'https://acme.com/activate?token=8f2c1e',
},
});email = client.emails.send({
"from": "Acme <hello@acme.com>",
"to": "ada@example.com",
"template": "welcome",
"variables": {
"first_name": "Ada",
"activation_url": "https://acme.com/activate?token=8f2c1e"
}
})curl -X POST https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "Acme Billing <billing@acme.com>",
"to": "ada@example.com",
"subject": "Your invoice INV-1042",
"html": "<img src=\"cid:logo\"><p>Your invoice is attached.</p>",
"attachments": [
{
"filename": "INV-1042.pdf",
"content": "JVBERi0xLjQKJcOkw7zDqc...",
"content_type": "application/pdf"
},
{
"filename": "logo.png",
"url": "https://acme.com/assets/logo.png",
"content_id": "logo"
}
]
}'import { readFile } from 'node:fs/promises';
const pdf = await readFile('INV-1042.pdf');
const email = await emailit.emails.send({
from: 'Acme Billing <billing@acme.com>',
to: 'ada@example.com',
subject: 'Your invoice INV-1042',
html: '<img src="cid:logo"><p>Your invoice is attached.</p>',
attachments: [
{
filename: 'INV-1042.pdf',
content: pdf.toString('base64'),
content_type: 'application/pdf',
},
{
filename: 'logo.png',
url: 'https://acme.com/assets/logo.png',
content_id: 'logo',
},
],
});import base64
with open("INV-1042.pdf", "rb") as f:
pdf = base64.b64encode(f.read()).decode()
email = client.emails.send({
"from": "Acme Billing <billing@acme.com>",
"to": "ada@example.com",
"subject": "Your invoice INV-1042",
"html": '<img src="cid:logo"><p>Your invoice is attached.</p>',
"attachments": [
{"filename": "INV-1042.pdf", "content": pdf, "content_type": "application/pdf"},
{"filename": "logo.png", "url": "https://acme.com/assets/logo.png", "content_id": "logo"}
]
})curl -X POST https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reminder-appt-5531" \
-d '{
"from": "Acme <reminders@acme.com>",
"to": "ada@example.com",
"subject": "Your appointment tomorrow",
"text": "See you tomorrow at 2 PM.",
"scheduled_at": "2026-10-02T09:00:00Z",
"meta": { "appointment_id": "5531" }
}'const email = await emailit.emails.send({
from: 'Acme <reminders@acme.com>',
to: 'ada@example.com',
subject: 'Your appointment tomorrow',
text: 'See you tomorrow at 2 PM.',
scheduled_at: '2026-10-02T09:00:00Z',
meta: { appointment_id: '5531' },
});email = client.emails.send({
"from": "Acme <reminders@acme.com>",
"to": "ada@example.com",
"subject": "Your appointment tomorrow",
"text": "See you tomorrow at 2 PM.",
"scheduled_at": "2026-10-02T09:00:00Z",
"meta": {"appointment_id": "5531"}
}){
"object": "email",
"id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"ids": {
"ada@example.com": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"grace@example.com": "em_4KfIXZSV8v8L1k0Mg2YDR3rytvj"
},
"token": "4KDY1iIpQSSW5pAoiz3JEfqcsO0",
"message_id": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"from": "Acme <hello@acme.com>",
"to": ["ada@example.com", "grace@example.com"],
"subject": "Welcome to Acme",
"status": "accepted",
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"tracking": {
"loads": true,
"clicks": true
}
}{
"object": "email",
"id": "em_4K76IA5sFNIsLXW9QC2ro8cDbOj",
"token": "4KWzEED2cnej6UMjF4v508VqQIp",
"message_id": "<4KWzEED2cnej6UMjF4v508VqQIp@acme.com>",
"from": "Acme <reminders@acme.com>",
"to": ["ada@example.com"],
"subject": "Your appointment tomorrow",
"status": "scheduled",
"scheduled_at": "2026-10-02T09:00:00.000Z",
"created_at": "2026-10-01T09:30:12.482913Z",
"tracking": {
"loads": false,
"clicks": false
}
}{
"error": "Validation failed",
"validation_errors": [
"Missing required field: subject",
"Invalid to email address at index 1: grace@example"
]
}{
"error": "Insufficient credits",
"message": "Insufficient credits to send this email. Required: 2, available: 0."
}{
"code": "unverified_workspace_recipient",
"error": "Workspace not verified",
"message": "Unverified workspaces can only send to workspace members' account emails. Blocked recipient: grace@example.com.",
"blocked_recipients": ["grace@example.com"]
}{
"error": "Domain not authorized",
"message": "API key is not authorized to send from this domain"
}{
"error": "Template not found",
"message": "Template 'welcome' not found or not published"
}{
"error": "Message too large",
"message": "Message size (41.27MB) exceeds maximum allowed size of 40MB"
}{
"error": "Domain not verified"
}{
"error": "Rate limit exceeded",
"message": "Too many requests. Maximum 2 messages per second allowed.",
"limit": 2,
"current": 2,
"retry_after": 1
}List emails
Returns a page of emails, newest first. By default the list shows outgoing emails from the last 14 days.
/emailsRequires a full API key. Each recipient of a send is a separate email in this list.
Query parameters
pageintegerdefault: 1limitintegerdefault: 25typestringdefault: outbounddate_fromstringOnly emails created on or after this date, such as 2026-08-01 (from 00:00 UTC). Without it, the list starts 14 days ago. created_at filters don’t change this window.
date_tostringsearchstringmatchstringdefault: allall or or. How the filters below combine.orderstringdirectionstringasc or desc.Filters
Add filters as key.condition=value, for example status.exact=bounced or created_at.after=2026-09-01. See Filtering for the conditions of each type.
| Key | Type | Values and notes |
|---|---|---|
to |
string | Recipient address. |
from |
string | Sender as sent, including any display name. |
subject |
string | |
status |
enum | accepted, scheduled, delivered, loaded, clicked, attempted, bounced, failed, rejected, suppressed, received, complained, canceled, held |
tag |
string | The email’s tag. Sending through the API or SMTP doesn’t set a tag at the moment. |
spam_score |
number | |
created_at |
date | |
updated_at |
date | |
api_key_id |
string | ID of the API key that sent the email (key_…). |
sending_domain_id |
string | ID of the sending domain (dom_…). |
Every key is also a sort key. The older status, rcpt_to, mail_from, subject, api_key_id and sending_domain_id query parameters still work: status matches exactly and the address and subject parameters match partially.
Returns
Returns a data array of email objects with next_page_url and previous_page_url. See Pagination. The page URLs don’t carry your filters, so request the next page with your own parameters and page increased by one.
objectstringemail.idstringtypestringoutbound or inbound.fromstringtostringsubjectstringstatusstringsizeintegerscheduled_atstring | nullnull.created_atstringupdated_atstringmetaobject | nullmeta you sent.curl -G https://api.emailit.com/v2/emails \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-d status.exact=bounced \
-d status.exact=failed \
-d match=or \
-d date_from=2026-09-01 \
-d order=created_at \
-d direction=desc{
"data": [
{
"object": "email",
"id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"type": "outbound",
"from": "Acme <hello@acme.com>",
"to": "ada@example.com",
"subject": "Welcome to Acme",
"status": "delivered",
"size": 4523,
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"updated_at": "2026-10-01T09:30:14.118204Z",
"meta": null
},
{
"object": "email",
"id": "em_4KfIXZSV8v8L1k0Mg2YDR3rytvj",
"type": "outbound",
"from": "Acme <hello@acme.com>",
"to": "grace@example.com",
"subject": "Welcome to Acme",
"status": "loaded",
"size": 4527,
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"updated_at": "2026-10-01T09:41:03.770521Z",
"meta": null
}
],
"next_page_url": "/app/v2/emails?page=2&limit=25",
"previous_page_url": null
}{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation error",
"details": [
{
"instancePath": "/limit",
"schemaPath": "#/properties/limit/maximum",
"keyword": "maximum",
"params": { "comparison": "<=", "limit": 100 },
"message": "must be <= 100"
}
]
}Retrieve an email
Retrieves an email with its status, parsed headers, HTML and text body, and attachments.
/emails/{id}Requires a full API key. Message content is kept for your plan’s content retention period. After that, headers, body and attachments are empty, and the status and metadata remain. To fetch only part of an email, use Retrieve the body, Retrieve metadata, List attachments or Retrieve raw MIME.
Path parameters
idstringrequiredem_4KYof1ZzXndZE2VPi0DgULiekG8.Returns
Returns the email object.
objectstringemail.idstringtypestringoutbound for emails you sent, inbound for emails you received.tokenstringmessage_idstringMessage-ID header.fromstringAcme <hello@acme.com>.tostringsubjectstringstatusstringThe current status: accepted, scheduled, delivered, loaded, clicked, attempted, bounced, failed, rejected, suppressed, received, complained, canceled or held. See Email statuses.
sizeintegerscheduled_atstring | nullnull.created_atstringupdated_atstringtrackingobjectloads) and click (clicks) tracking is on.metaobject | nullmeta you sent, or null.headersobject | nullnull once the content is purged.bodyobjecttext and html, each a string or null.attachmentsobject[]The attachments, each with filename, content_type, size in bytes, content_id (for inline files), content_disposition (attachment or inline) and content (Base64).
{
"object": "email",
"id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"type": "outbound",
"token": "4KDY1iIpQSSW5pAoiz3JEfqcsO0",
"message_id": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"from": "Acme Billing <billing@acme.com>",
"to": "ada@example.com",
"subject": "Your invoice INV-1042",
"status": "delivered",
"size": 48213,
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"updated_at": "2026-10-01T09:30:14.118204Z",
"tracking": {
"loads": true,
"clicks": true
},
"meta": {
"invoice_id": "INV-1042"
},
"headers": {
"From": "Acme Billing <billing@acme.com>",
"To": "ada@example.com",
"Subject": "Your invoice INV-1042",
"Message-ID": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"MIME-Version": "1.0",
"Content-Type": "multipart/mixed; boundary=\"--_NmP-4f1c2a9e7b3d0e5f-Part_1\""
},
"body": {
"text": "Your invoice is attached.",
"html": "<p>Your invoice is attached.</p>"
},
"attachments": [
{
"filename": "INV-1042.pdf",
"content_type": "application/pdf",
"size": 40960,
"content_id": null,
"content_disposition": "attachment",
"content": "JVBERi0xLjQKJcOkw7zDqc..."
}
]
}{
"object": "email",
"id": "em_4KbeG9poqTmjvQZeN8pMoJkCVNd",
"type": "inbound",
"token": "4KTnDU5PzzqDqp8UWb9qVhPVFOT",
"message_id": "<CAH7x2k9@mail.example.com>",
"from": "Ada Lovelace <ada@example.com>",
"to": "support@inbound.acme.com",
"subject": "Re: Your invoice INV-1042",
"status": "received",
"size": 8234,
"scheduled_at": null,
"created_at": "2026-10-01T11:02:45.031877Z",
"updated_at": "2026-10-01T11:02:45.031877Z",
"meta": null,
"headers": {
"From": "Ada Lovelace <ada@example.com>",
"To": "support@inbound.acme.com",
"Subject": "Re: Your invoice INV-1042",
"Content-Type": "text/plain; charset=utf-8"
},
"body": {
"text": "Thanks, received.",
"html": null
},
"attachments": []
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}Retrieve raw MIME
Retrieves the full MIME source of an email as stored by Emailit, along with its metadata.
/emails/{id}/rawRequires a full API key. Use it to archive a message, debug its structure or parse it with your own MIME library. Once the content retention period ends, raw and headers are null.
Path parameters
idstringrequiredReturns
Returns the email’s metadata, as in Retrieve metadata but without attachments, plus the raw message.
rawstring | nullnull once the content is purged.headersobject | nullThe other fields (object, id, type, token, message_id, from, to, subject, status, size, scheduled_at, created_at, updated_at, tracking and meta) are the same as in Retrieve an email.
{
"object": "email",
"id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"type": "outbound",
"token": "4KDY1iIpQSSW5pAoiz3JEfqcsO0",
"message_id": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"from": "Acme <hello@acme.com>",
"to": "ada@example.com",
"subject": "Welcome to Acme",
"status": "delivered",
"size": 1342,
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"updated_at": "2026-10-01T09:30:14.118204Z",
"tracking": {
"loads": false,
"clicks": false
},
"meta": null,
"headers": {
"From": "Acme <hello@acme.com>",
"To": "ada@example.com",
"Subject": "Welcome to Acme",
"Message-ID": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"MIME-Version": "1.0",
"Content-Type": "text/html; charset=utf-8"
},
"raw": "From: Acme <hello@acme.com>\r\nTo: ada@example.com\r\nSubject: Welcome to Acme\r\nMessage-ID: <4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>\r\nMIME-Version: 1.0\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}List attachments
Returns the attachments of an email, including their content.
/emails/{id}/attachmentsRequires a full API key. Works for outgoing and inbound emails. Inline images (parts with a Content-ID) are included. To get the list without the file contents, use Retrieve metadata. Once the content retention period ends, the list is empty.
Path parameters
idstringrequiredReturns
Returns a list object with every attachment. The list isn’t paginated.
objectstringlist.dataobject[]data[].filenamestringdata[].content_typestringapplication/pdf.data[].sizeintegerdata[].content_idstring | nullContent-ID of an inline attachment, or null.data[].content_dispositionstring | nullattachment or inline.data[].contentstring{
"object": "list",
"data": [
{
"filename": "INV-1042.pdf",
"content_type": "application/pdf",
"size": 40960,
"content_id": null,
"content_disposition": "attachment",
"content": "JVBERi0xLjQKJcOkw7zDqc..."
},
{
"filename": "logo.png",
"content_type": "image/png",
"size": 5120,
"content_id": "logo",
"content_disposition": "inline",
"content": "iVBORw0KGgoAAAANSUhEUgAA..."
}
]
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}Retrieve the body
Returns the HTML and plain-text body of an email, decoded from its MIME parts.
/emails/{id}/bodyRequires a full API key. Works for outgoing and inbound emails. For outgoing emails, the body is what was sent, after template and variable rendering. Once the content retention period ends, both fields are null.
Path parameters
idstringrequiredReturns
textstring | nullnull if the email has none.htmlstring | nullnull if the email has none.{
"text": "Welcome!\n\nThanks for signing up.",
"html": "<h1>Welcome!</h1><p>Thanks for signing up.</p>"
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}Retrieve metadata
Retrieves an email without its body: status, headers, your meta data and the list of attachments without their contents.
/emails/{id}/metaRequires a full API key. This is the lightest way to read an email’s details when you don’t need the content.
Path parameters
idstringrequiredReturns
Returns the same fields as Retrieve an email, without body, and with attachments described but not included:
attachmentsobject[]filename, content_type, size, content_id and content_disposition. No content.headersobject | nullnull once the content is purged.metaobject | nullmeta you sent with the email.{
"object": "email",
"id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"type": "outbound",
"token": "4KDY1iIpQSSW5pAoiz3JEfqcsO0",
"message_id": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"from": "Acme Billing <billing@acme.com>",
"to": "ada@example.com",
"subject": "Your invoice INV-1042",
"status": "delivered",
"size": 48213,
"scheduled_at": null,
"created_at": "2026-10-01T09:30:12.482913Z",
"updated_at": "2026-10-01T09:30:14.118204Z",
"tracking": {
"loads": true,
"clicks": true
},
"meta": {
"invoice_id": "INV-1042"
},
"headers": {
"From": "Acme Billing <billing@acme.com>",
"To": "ada@example.com",
"Subject": "Your invoice INV-1042",
"Message-ID": "<4KDY1iIpQSSW5pAoiz3JEfqcsO0@acme.com>",
"MIME-Version": "1.0",
"Content-Type": "multipart/mixed; boundary=\"--_NmP-4f1c2a9e7b3d0e5f-Part_1\""
},
"attachments": [
{
"filename": "INV-1042.pdf",
"content_type": "application/pdf",
"size": 40960,
"content_id": null,
"content_disposition": "attachment"
}
]
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}Update a scheduled email
Moves a scheduled email to a new send time.
/emails/{id}Works with sending and full API keys. You can only reschedule an email whose status is scheduled and whose current send time is more than 3 minutes away. Only the send time can change; to change the content, cancel the email and send a new one.
A scheduled send to several recipients creates one email per recipient. Reschedule each ID from the ids map of the send response.
Path parameters
idstringrequiredBody parameters
scheduled_atstringrequiredThe new send time, as an ISO 8601 date-time such as 2026-10-03T09:00:00Z or in English such as tomorrow at 3pm. It must be more than 3 minutes in the future.
Returns
objectstringemail.idstringstatusstringscheduled.scheduled_atstringupdated_atstringmessagestringReturns 422 if the email isn’t scheduled, is due within 3 minutes, or the new time can’t be parsed or is too soon.
curl -X POST https://api.emailit.com/v2/emails/em_4K76IA5sFNIsLXW9QC2ro8cDbOj \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"scheduled_at": "tomorrow at 3pm"}'{
"object": "email",
"id": "em_4K76IA5sFNIsLXW9QC2ro8cDbOj",
"status": "scheduled",
"scheduled_at": "2026-10-03T09:00:00.000Z",
"updated_at": "2026-10-01T10:15:40.207316Z",
"message": "Email schedule has been updated successfully"
}{
"error": "Email not found",
"message": "Email with ID 'em_4K76IA5sFNIsLXW9QC2ro8cDbOj' not found in your workspace"
}{
"error": "Cannot update email",
"message": "Email cannot be updated. Current status: 'delivered'. Only 'scheduled' emails can be updated."
}{
"error": "Cannot update email",
"message": "Scheduled emails can only be updated at least 3 minutes before the scheduled time. This email is scheduled to send in 2 minute(s)."
}{
"error": "Invalid scheduled_at",
"message": "The new scheduled time must be at least 3 minutes in the future."
}Cancel an email
Removes an email from the send queue and sets its status to canceled.
/emails/{id}/cancelWorks with sending and full API keys. Cancel is best effort: it pulls the email out of the queue, but if a delivery attempt has already started, that attempt may still complete and only the remaining retries are stopped. The response tells you which case applies in in_flight. Canceling fires an email.canceled event, and the credit isn’t refunded. The dashboard’s Cancel delivery action does the same thing.
| Status | Can cancel | Notes |
|---|---|---|
scheduled |
Yes | Until 3 minutes before the scheduled time. |
accepted |
Yes | Queued and not yet delivered. |
attempted |
Yes | Stops the remaining retries after a temporary failure. |
| Any other | No | The email was already delivered, failed, or canceled. |
To cancel a send with several recipients, cancel each ID from the ids map of the send response.
Path parameters
idstringrequiredReturns
objectstringemail.idstringstatusstringcanceled.in_flightbooleantrue if a delivery attempt may already be under way and could still complete. false if the email was removed from the queue before any attempt.messagestring{
"object": "email",
"id": "em_4K76IA5sFNIsLXW9QC2ro8cDbOj",
"status": "canceled",
"in_flight": false,
"message": "Email has been canceled and removed from the send queue."
}{
"object": "email",
"id": "em_4K76IA5sFNIsLXW9QC2ro8cDbOj",
"status": "canceled",
"in_flight": true,
"message": "Email was canceled. The current delivery attempt may still complete; remaining retries were stopped."
}{
"error": "Email not found",
"message": "Email with ID 'em_4K76IA5sFNIsLXW9QC2ro8cDbOj' not found in your workspace"
}{
"error": "Cannot cancel email",
"message": "Email cannot be canceled. Current status: 'delivered'. Only 'scheduled', 'accepted', or 'attempted' emails can be canceled."
}{
"error": "Cannot cancel email",
"message": "Scheduled emails can only be canceled at least 3 minutes before the scheduled time. This email is scheduled to send in 2 minute(s)."
}Retry an email
Queues a copy of an email that didn’t get through. The copy is a new email with its own ID, and the original keeps its status.
/emails/{id}/retryWorks with sending and full API keys. The copy has the same sender, recipient, subject, content, headers, meta and tracking settings, with a new Message-ID. It costs credits like a new send: one credit, or two for a campaign email.
You can retry an email when:
- Its status is
bounced,failed,suppressedorheld. - It was created in the last 30 days.
- Its content hasn’t been purged by your retention period, and its sending domain still exists.
Fix the cause first. A suppressed address that’s still on your suppression list is suppressed again, and a held email is held again until the reason it was held is resolved.
Path parameters
idstringrequiredReturns
objectstringemail.idstringoriginal_idstringtokenstringmessage_idstringMessage-ID.fromstringtostringsubjectstringstatusstringaccepted.created_atstringmessagestring{
"object": "email",
"id": "em_4KbeG9poqTmjvQZeN8pMoJkCVNd",
"original_id": "em_4KKrQ7TzsVtzsS8zG069B2aMtoK",
"token": "4KTnDU5PzzqDqp8UWb9qVhPVFOT",
"message_id": "<4KTnDU5PzzqDqp8UWb9qVhPVFOT@acme.com>",
"from": "Acme <hello@acme.com>",
"to": "ada@example.com",
"subject": "Welcome to Acme",
"status": "accepted",
"created_at": "2026-10-01T12:04:51.330482Z",
"message": "Email has been queued for retry"
}{
"error": "Insufficient credits",
"message": "Insufficient credits to retry this email. Required: 1, available: 0."
}{
"error": "Email not found",
"message": "Email with ID 'em_4KKrQ7TzsVtzsS8zG069B2aMtoK' not found in your workspace"
}{
"error": "Cannot retry email",
"message": "Only bounced, failed, suppressed, or held emails can be retried. Current status: 'delivered'"
}{
"error": "Cannot retry email",
"message": "Emails older than 30 days cannot be retried"
}{
"error": "Cannot retry email",
"message": "Email raw content has been purged and can no longer be retried"
}Forward an email
Sends the content of an outgoing email to new recipients as a new email. The original email is unchanged.
/emails/{id}/forwardWorks with sending and full API keys. By default the forward is a plain resend of the original HTML, text and attachments. Set include_headers to add a “Forwarded message” block and an optional note above the original content.
A forward is a new send, so the rules of Send an email apply: the from address must be on a verified sending domain, each recipient costs a credit and counts against the sending limits, tracking follows the domain’s settings, and the Idempotency-Key header is supported. On top of that, a workspace can forward at most 3 emails per hour.
Only outgoing emails can be forwarded, and only while their content is kept by your retention period. To forward received mail, use an automation.
Path parameters
idstringrequiredHeaders
Idempotency-KeystringBody parameters
tostring | string[]requiredinclude_headersbooleandefault: falseWhen true, adds a “Forwarded message” block with the original sender, date, subject and recipient, and your note above it. When false, resends the original content unchanged.
commentstringinclude_headers. body is accepted as an alias.htmlstringcomment in the HTML part. Only used with include_headers.textstringcomment in the text part. Only used with include_headers.fromstringfrom.subjectstringFwd: and the original subject with include_headers.Original attachments are included when their file type is allowed.
Returns
Returns the same object as Send an email, with two extra fields:
original_idstringmessagestringOver the forward limit, the API returns 429 with a retry-after header.
curl -X POST https://api.emailit.com/v2/emails/em_4KYof1ZzXndZE2VPi0DgULiekG8/forward \
-H "Authorization: Bearer $EMAILIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: fwd-inv-1042-grace" \
-d '{
"to": ["grace@example.com"],
"include_headers": true,
"comment": "Grace, here is the invoice Ada asked about."
}'const email = await emailit.emails.forward('em_4KYof1ZzXndZE2VPi0DgULiekG8', {
to: ['grace@example.com'],
include_headers: true,
comment: 'Grace, here is the invoice Ada asked about.',
});email = client.emails.forward("em_4KYof1ZzXndZE2VPi0DgULiekG8", {
"to": ["grace@example.com"],
"include_headers": True,
"comment": "Grace, here is the invoice Ada asked about."
}){
"object": "email",
"id": "em_4K76IA5sFNIsLXW9QC2ro8cDbOj",
"original_id": "em_4KYof1ZzXndZE2VPi0DgULiekG8",
"token": "4KWzEED2cnej6UMjF4v508VqQIp",
"message_id": "<4KWzEED2cnej6UMjF4v508VqQIp@acme.com>",
"from": "Acme Billing <billing@acme.com>",
"to": ["grace@example.com"],
"subject": "Fwd: Your invoice INV-1042",
"status": "accepted",
"scheduled_at": null,
"created_at": "2026-10-01T13:20:07.915203Z",
"tracking": {
"loads": true,
"clicks": true
},
"message": "Email has been queued for forwarding"
}{
"error": "Validation failed",
"validation_errors": ["Invalid to email address at index 0: grace@example"]
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}{
"error": "Cannot forward email",
"message": "Only outgoing emails can be forwarded"
}{
"error": "Cannot forward email",
"message": "Email raw content has been purged and can no longer be forwarded"
}{
"error": "too_many_requests",
"message": "Forwarding is limited to 3 emails per hour for this workspace. Try again later.",
"limit": 3,
"current": 4,
"retry_after": 2711
}Retrieve status only
Returns only the current status of an email.
/email/{id}Requires a full API key. Note the singular /email in the path. The response is small, which makes this endpoint handy for quick status checks. For status changes as they happen, use webhooks instead of polling.
Path parameters
idstringrequiredReturns
statusstringThe current status: accepted, scheduled, delivered, loaded, clicked, attempted, bounced, failed, rejected, suppressed, received, complained, canceled or held. See Email statuses.
{
"status": "delivered"
}{
"error": "Email not found",
"message": "Email with ID 'em_4KYof1ZzXndZE2VPi0DgULiekG8' not found in your workspace"
}