Send an email
Sends mail from the workspace user the key acts as. Requires mail.send, which is deliberately separate from mail.messages.write.
Permission
The key must be granted mail.send
A key without it gets 403 insufficient_scope, with the missing permission named in details.
Answers 202, not 200: the message is queued and archived in Sent, and delivery happens afterwards. A 202 does not mean it arrived, and this API cannot tell you that it did.
Request body
| Field | Type | Description |
|---|---|---|
to required |
string[] | Recipients. At least one. |
subject required |
string | |
text |
string | Plain-text body. Required unless html is given. |
html |
string | HTML body, sanitised before sending. Required unless text is given. |
from |
string |
Any address you may send as: an alias, or a list you are an authorised sender of. Anything else is refused.
Defaults to your mailbox address
|
in_reply_to |
string | The parent's message_id, angle brackets included. The References chain is built for you. |
attachments |
object[] | Files to send along with the message. Its fields are below. |
Each item in attachments
| Field | Type | Description |
|---|---|---|
filename required |
string | As the recipient will see it. |
content_type required |
string | For example application/pdf. |
content_base64 required |
string | The file, base64 encoded. Padded or not, standard or URL-safe. |
inline |
boolean |
True for an image the HTML body displays rather than a file to download.
Defaults to false
|
content_id |
string | Required when inline is true. The HTML refers to it as src="cid:the-id". |
Request
curl https://api.veruapis.com/v1/messages/send \
-X POST \
-H "Authorization: Bearer vak_live_..." \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"to": ["[email protected]"],
"subject": "Quarterly numbers",
"text": "The numbers are attached."
}'
Idempotency-Key is required on every write. Retrying with the same key returns the original response rather than repeating the operation, so a timeout is something you retry rather than something you investigate.
Response
Answers 202.
| Field | Type | Description |
|---|---|---|
message_id |
string | The Message-Id assigned to the sent mail. |
queued |
integer | How many queue entries were created. |
status |
string |
queued
|
{
"data": {
"message_id": "<[email protected]>",
"queued": 1,
"status": "queued"
},
"meta": { "request_id": "req_01j8z9...", "timestamp": "2026-09-02T10:00:00Z" }
}
Answers 202, not 200: the message is queued for delivery and archived in Sent, and delivery happens afterwards. A 202 does not mean it arrived, and this API has no way to know that it did.
Every failure uses the same shape, with a stable string code you can branch on. See the error reference.
Related
- POST Create a draft
- POST Create a mail filter
- POST Create a mail folder
- DELETE Delete a draft
- DELETE Delete a mail filter
- DELETE Delete a mail folder
- DELETE Delete a message
- GET Download an attachment
- GET Get a message
- GET Get display preferences
- GET List attachments
- GET List drafts
- GET List mail filters
- GET List mail folders
- GET List messages
- POST Move messages
- PUT Replace a draft
- POST Change flags and labels
- PATCH Rename a mail folder
- PATCH Update display preferences