Send an email
You can send an email about an entity using the REST API.
Overview
The workspace-level mails resource is available when the referenced entity type supports the mailing feature. The email is sent on behalf of the calling user, who is automatically added to the CC list.
Required permission: Send mail.
Endpoint
POST .../api/shared_spaces/{space_id}/workspaces/{workspace_id}/mails
For example, to send an email about defect 1042:
POST .../api/shared_spaces/1001/workspaces/1002/mails
Request
The request body is a JSON object with a data array. Each item in the array defines one email to send. You can batch multiple emails in a single request.
The top-level request object is a MailsCollection, and each item in data is a MailJson object.
Request body fields
| Field | Description |
|---|---|
subject
|
Email subject line. |
message
|
Email body. HTML is supported. Used as comment body when add_as_comment is true. |
mail_entities
|
Required. The entities whose details are rendered in the email. Each item requires id and type. At least one entity is required. Duplicates are removed. |
to_recipients
|
Primary recipients. See Recipient format below. At least one recipient is required across to_recipients, cc_recipients, and bcc_recipients. |
cc_recipients
|
CC recipients. The calling user is added to CC automatically. |
bcc_recipients
|
BCC recipients. |
include_attachments
|
If true, the entity's file attachments are included in the email. Default: false. |
separated_emails
|
If true, one email is sent per entity instead of a combined email, and subject is reset to null. Default: false. |
add_as_comment
|
If true, the message is also added as a comment on each entity that supports comments. Requires comment creation permission. Skipped if message is blank after HTML stripping. |
mail_importance
|
Written to the MIME Importance header as provided. Common values: High, Normal, Low. |
should_check_recipients_limit
|
If true, validates the total number of recipients against the configured site limit. Default: false (limit is not enforced). |
Validation rules:
subjectmust be non-empty. Newlines are replaced with spaces.- At least one recipient must exist across
to_recipients,cc_recipients, andbcc_recipients. mail_entities.datamust contain at least one entity.
Recipient format
Each recipient object in a to_recipients, cc_recipients, or bcc_recipients list uses one of the following formats:
| Recipient type | Format |
|---|---|
| Workspace user | { "id": 1001, "type": "workspace_user" }
|
| Team | { "id": 55, "type": "team" } — all team members receive the email. |
| External address | { "type": "external_email", "email": "user@example.com" } — only available if external email is enabled for the site. |
Recipient rules:
- Internal recipient:
idis set,emailis blank, andtypeis notexternal_email. - External recipient:
idis null,emailis non-blank, andtypeisexternal_email. type: "team"expands to team members. Any other non-external type is treated as a user ID type.
Example
Send an email about defect 1042 to a workspace user and a team, with an external BCC:
POST .../api/shared_spaces/1001/workspaces/1002/mails
Content-Type: application/json
{
"data": [
{
"subject": "Defect #1042 - Login crash on Safari",
"message": "<p>Please review this defect.</p>",
"mail_entities": {
"data": [
{ "id": 1042, "type": "defect" }
]
},
"to_recipients": {
"data": [
{ "id": 1001, "type": "workspace_user" }
]
},
"cc_recipients": {
"data": [
{ "id": 55, "type": "team" }
]
},
"bcc_recipients": {
"data": [
{ "type": "external_email", "email": "external@example.com" }
]
},
"include_attachments": false,
"add_as_comment": false,
"mail_importance": "High"
}
]
}
Send with file attachments
To include file attachments in the email, send a multipart/form-data request instead of JSON. The request must include:
- A part named
mailcontaining the JSON payload (Content-Type:application/json). - One part named
attachmentsper file to attach.
Example:
POST .../api/shared_spaces/1001/workspaces/1002/mails
Content-Type: multipart/form-data; boundary=a1b2c3d4
--a1b2c3d4
Content-Disposition: form-data; name="mail"
Content-Type: application/json
{
"data": [
{
"subject": "Defect #1042",
"message": "<p>See attached report.</p>",
"mail_entities": { "data": [ { "id": 1042, "type": "defect" } ] },
"to_recipients": { "data": [ { "id": 1001, "type": "workspace_user" } ] }
}
]
}
--a1b2c3d4
Content-Disposition: form-data; name="attachments"; filename="report.pdf"
Content-Type: application/pdf
--a1b2c3d4--
Note: The boundary value (a1b2c3d4 in the example) is arbitrary — use any unique string that does not appear in the body content. Your HTTP client or library reads the file from disk and embeds the bytes automatically when you specify the file path.
Note: Attachment filenames are sanitized ([=+;:'"\[{}\\/|?, ] becomes _) and prefixed with a UUID. Upload size is capped by the STORAGE_MAX_FILE_SIZE site parameter.
Response
The response returns a MailsCollection object with total_count, data (successful mails only), and errors (when failures exist).
Each item in response data is a MailJson object.
Per-mail failures are not fatal to the entire request. Failed mails are removed from data and appended to errors. A partial failure can still return HTTP 200.
External recipients are controlled by ALLOWED_EXTERNAL_EMAIL_DOMAINS (* allows all; empty rejects all external addresses). Sender auto-copy is controlled by SEND_EMAIL_ALWAYS_COPY_SENDER.
Common error codes:
| Error code | Description |
|---|---|
platform.send_mail_error
|
Missing or empty required field (such as subject, mail_entities, or recipients). |
platform.general_error
|
An unexpected error occurred while sending the email. |

