> ## 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.

<CodeGroup>
  ```bash JSON theme={null}
  curl -X POST "$EE_URL/api/v1/emails" \
    -H "X-API-KEY: $EE_KEY" -H "Content-Type: application/json" \
    -d '{
      "account_id": "acc_ba1fa6938d8548298e8ce62e4b4b4e99",
      "to": [{ "display_name": "Ana Lee", "identifier": "ana@example.com" }],
      "cc": ["team@example.com"],
      "subject": "Proposal",
      "body": "<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/emails" \
    -H "X-API-KEY: $EE_KEY" \
    -F account_id=acc_ba1fa6938d8548298e8ce62e4b4b4e99 \
    -F 'to=[{"display_name":"Ana Lee","identifier":"ana@example.com"}]' \
    -F subject=Proposal \
    --form-string 'body=<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/emails", [
      'headers' => ['X-API-KEY' => $apiKey],
      'multipart' => [
          ['name' => 'account_id', 'contents' => $accountId], // acc_…
          ['name' => 'to', 'contents' => json_encode([['identifier' => 'ana@example.com']])],
          ['name' => 'subject', 'contents' => 'Proposal'],
          ['name' => 'body', '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 'body=<p>…'` treats a value starting with `<` as a file name.
</Tip>

## Fields

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

<ParamField body="to, cc, bcc" type="attendee[]">
  Recipients as `[{ "display_name": "Ana", "identifier": "ana@example.com" }]`, a list of addresses, or one comma-separated string. At least one recipient is required (except for replies).
</ParamField>

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

<ParamField body="body" type="string">HTML body. A text version is made from it automatically.</ParamField>
<ParamField body="body_plain" type="string">Plain-text body, if you want to set it yourself.</ParamField>

<ParamField body="from" type="attendee">
  `display_name` changes the sender's name. `identifier` sends from an alias; the provider must allow it (a Gmail "Send mail as" address, for example).
</ParamField>

<ParamField body="reply_to" type="string">The `email_…` id of an email to answer. See [Replies and threads](/emails/replies-and-threads).</ParamField>
<ParamField body="reply_to_addresses" type="attendee[]">Addresses for the `Reply-To` header.</ParamField>

<ParamField body="custom_headers" type="object[]">
  `[{ "name": "X-Campaign", "value": "q4" }]`. Allowed: any `X-…` header, `List-Unsubscribe`, `List-Unsubscribe-Post`, and `Reply-To`. `Content-Type` is accepted and ignored.
</ParamField>

<ParamField body="tracking_options" type="object">`{ "opens": true, "links": true, "label": "…" }`. See [Tracking](/emails/tracking).</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, send `to`, `cc`, `bcc`, `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.

## 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`.
* `provider_id` is `null`: SMTP doesn't return an id when sending. The Sent copy gets its ids 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 and `origin: "api"`.

| Error | When |
| - | - |
| `400 errors/invalid_parameters` | A field is wrong, for example no recipient, or `account_id` isn't an `acc_…` 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. Check the Sent folder (`GET /api/v1/emails?account_id=acc_…&role=SENT`) for the `message_id` first.
</Warning>


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