> ## Documentation Index
> Fetch the complete documentation index at: https://pigeonpost-developer.27communication.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Send an email

> Send from the user's own mailbox, with attachments, custom headers and tracking.

The email goes out through the provider's SMTP server (Gmail included), from the user's own address, and lands in their Sent folder. The account goes in the path.

<CodeGroup>
  ```bash JSON theme={null}
  curl -X POST "$EE_URL/api/v1/acc_ba1fa6938d8548298e8ce62e4b4b4e99/emails/send" \
    -H "X-API-KEY: $EE_KEY" -H "Content-Type: application/json" \
    -d '{
      "to": [{ "display_name": "Ana Lee", "email": "ana@example.com" }],
      "cc": [{ "email": "team@example.com" }],
      "subject": "Proposal",
      "html": "<p>Hi Ana,</p><p>Here is the proposal.</p>",
      "attachments": [{ "filename": "proposal.pdf", "content_type": "application/pdf", "content": "JVBERi0xLjQK…" }]
    }'
  ```

  ```bash Multipart (files) theme={null}
  curl -X POST "$EE_URL/api/v1/acc_ba1fa6938d8548298e8ce62e4b4b4e99/emails/send" \
    -H "X-API-KEY: $EE_KEY" \
    -F 'to[0][email]=ana@example.com' \
    -F 'to[0][display_name]=Ana Lee' \
    -F subject=Proposal \
    --form-string 'html=<p>Hi Ana,</p><p>Here is the proposal.</p>' \
    -F 'attachments=@proposal.pdf;type=application/pdf'
  ```

  ```php PHP (Guzzle) theme={null}
  $http->post("$baseUrl/api/v1/$accountId/emails/send", [ // $accountId: acc_…
      'headers' => ['X-API-KEY' => $apiKey],
      'multipart' => [
          ['name' => 'to[0][email]', 'contents' => 'ana@example.com'],
          ['name' => 'subject', 'contents' => 'Proposal'],
          ['name' => 'html', 'contents' => '<p>Here is the proposal.</p>'],
          ['name' => 'attachments', 'contents' => fopen('proposal.pdf', 'r'), 'filename' => 'proposal.pdf'],
      ],
  ]);
  ```
</CodeGroup>

<Tip>
  With curl, use `--form-string` for HTML bodies: `-F 'html=<p>…'` treats a value starting with `<` as a file name.
</Tip>

## Fields

<ParamField path="account_id" type="string" required>The account to send from (`acc_…`), in the path.</ParamField>

<ParamField body="to, cc, bcc" type="recipient[]">
  Recipients as `[{ "email": "ana@example.com", "display_name": "Ana" }]`. `display_name` is optional. At least one recipient is required (except for replies).
</ParamField>

<ParamField body="subject" type="string" />

<ParamField body="html" type="string">HTML body.</ParamField>
<ParamField body="plain_text" type="string">Plain-text body. Made from `html` when you leave it out.</ParamField>

<ParamField body="from" type="object">
  `{ "display_name", "email" }`. `display_name` changes the sender's name. `email` sends from an alias; the provider must allow it (a Gmail "Send mail as" address, for example). See [Which addresses can send](#which-addresses-can-send).
</ParamField>

<ParamField body="reply_to_message_id" type="string">The email you answer: its `Message-ID` (from its `headers`), or its `email_…` id. See [Replies and threads](/emails/replies-and-threads).</ParamField>
<ParamField body="reply_to" type="recipient[]">Addresses for the `Reply-To` header. This is not the email you answer.</ParamField>

<ParamField body="custom_headers" type="object[]">
  `[{ "name": "X-Campaign", "value": "q4" }]`. Allowed: any `X-…` header, `List-Unsubscribe`, `List-Unsubscribe-Post`, `Auto-Submitted`, and `Reply-To`. `Content-Type` is accepted and ignored. `Auto-Submitted` marks an automatic email (RFC 3834): `auto-replied` for an auto-reply, `auto-generated` for other automatic mail; other values are refused. Mail servers and auto-responders don't answer such mail, so two auto-responders can't email each other forever.
</ParamField>

<ParamField body="tracking_options" type="object">`{ "opens": true, "links": true, "label": "…" }`. See [Tracking](/emails/tracking).</ParamField>

<ParamField body="metadata" type="string">
  Your own note about this email, up to 1000 characters, for example which user or rule sent it. It's never put in the email. It comes back on the email's Sent copy as `sent_by.metadata` (in `email.new`), so you can tell your own sends apart, like Messenger's echo metadata.
</ParamField>

<ParamField body="attachments" type="file[] | object[]">
  Multipart: files in fields named `attachments`. JSON: `[{ "filename", "content_type", "content" }]` with `content` in base64.
</ParamField>

In a multipart request, write recipients as `to[0][email]`, `to[0][display_name]`, `to[1][email]` and so on (the same for `cc`, `bcc` and `reply_to`). Send `tracking_options` and `custom_headers` as JSON text.

Requests are limited to **30 MB** in total, attachments included. Providers have their own limits, for example 25 MB for Gmail.

## Which addresses can send

`GET /api/v1/{account_id}/email-senders` lists the addresses you can put in `from`. For an IMAP account that's its own address: the one it connected with.

```bash theme={null}
curl "$EE_URL/api/v1/acc_ba1fa6938d8548298e8ce62e4b4b4e99/email-senders" -H "X-API-KEY: $EE_KEY"
```

```json Response theme={null}
{
  "data": [
    {
      "object": "EmailSender",
      "email": "support@example.com",
      "display_name": "Support",
      "is_primary": true,
      "verification_status": "verified"
    }
  ],
  "total_count": 1
}
```

`display_name` is there when the account has a name. `verification_status` is `verified`, `pending` or `unknown`; an IMAP account's own address is always `verified`.

## Response

```json 201 Created theme={null}
{
  "object": "EmailSent",
  "account_id": "acc_ba1fa6938d8548298e8ce62e4b4b4e99",
  "message_id": "84c3db9476d84b288bc6fa055a576764@mail-api.example.com",
  "provider_id": null,
  "tracking_id": null
}
```

* `message_id` identifies the email in every mailbox it reaches. Its Sent copy has the same `Message-ID` header.
* `provider_id` is `null`: SMTP doesn't return an id when sending. The Sent copy gets its `email_…` id when it syncs.
* `tracking_id` is a `trk_…` id when you use `tracking_options`, otherwise `null`.
* The Sent copy syncs a moment later, and you receive an `email.new` event with the full email, `origin: "api"`, `is_echo: false` and `sent_by` (the API key it was sent with and your `metadata`).

There's no forward call. To forward, send a new email with the original's content and files. See [Replies and threads](/emails/replies-and-threads#forward).

| Error | When |
| - | - |
| `400 errors/invalid_parameters` | A field is wrong, for example no recipient, or an unknown `reply_to_message_id` |
| `401 errors/invalid_credentials` | The provider rejected the account's credentials. Reconnect it |
| `404 errors/not_found` | No account with this id |
| `422 errors/provider_rejected` | The provider refused the email, for example because it's too large |
| `502 errors/provider_error` | The provider failed or didn't answer |

<Warning>
  If a send request times out on your side, don't retry blindly: the email may already be on its way. First check the Sent folder (`GET /api/v1/acc_…/folders/fld_…/emails`, with the id of the folder whose `role` is `SENT`) for an email whose `Message-ID` header matches.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.