> ## Documentation Index
> Fetch the complete documentation index at: https://agentheya.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# send_email

> Send email.

# send\_email

Sends a transactional email from the agent. The call returns a `message_id` you can pass to [`check_email_status`](/builtin-tools/check_email_status) to look up the final delivery state (delivered, bounced, complained, …) and an event-by-event timeline.

## Recipient allowlist (enforced)

`send_email` will only send to:

* The authenticated **subscriber**'s email address, OR
* The **agent owner**'s email address, OR
* Any address matched by `details.email_recipient_allowlist` — an array of full addresses or `@domain.com` wildcards configured in the agent's **Details** tab.

The check applies to **every** address in `to`, `cc`, and `bcc`. If even one entry fails, the whole call is rejected with the offending addresses listed in `details.rejected_to / rejected_cc / rejected_bcc`. There is no way to email an arbitrary stranger.

## Parameters

| Name                     | Type                          | Required | Description                                                                                                                     |
| ------------------------ | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `to`                     | `string` (email)              | yes      | Primary recipient. Must match the allowlist.                                                                                    |
| `cc`                     | `string[]` (max 50)           | no       | CC recipients. Each must match the allowlist.                                                                                   |
| `bcc`                    | `string[]` (max 50)           | no       | BCC recipients. Each must match the allowlist.                                                                                  |
| `subject`                | `string` (1–200)              | yes      | Subject line.                                                                                                                   |
| `body_text`              | `string` (1–50000)            | yes      | Plain-text body.                                                                                                                |
| `body_html`              | `string` (≤200000)            | no       | Optional HTML body.                                                                                                             |
| `reply_to`               | `string` (email)              | no       | Optional `Reply-To` header.                                                                                                     |
| `headers`                | `object` of `string → string` | no       | Custom headers map. See [Custom headers](#custom-headers).                                                                      |
| `scheduled_at`           | `string` (ISO 8601)           | no       | Hold the email until this UTC datetime. Mutually exclusive with `send_in`.                                                      |
| `send_in`                | `string` (delay)              | no       | Schedule via a delay expression. See [Scheduling](#scheduling).                                                                 |
| `attachments`            | `object[]` (max 20)           | no       | Files to attach. See [Attachments](#attachments).                                                                               |
| `in_reply_to_inbound_id` | `string`                      | no       | Mark this message as a reply to a previously-received email. See [Replying to a received email](#replying-to-a-received-email). |

## Custom headers

Pass arbitrary headers via the `headers` map:

```json theme={null}
{
  "headers": {
    "X-Campaign": "spring-2026",
    "List-Unsubscribe": "<https://example.com/unsubscribe?u=abc>",
    "References": "<thread-id@example.com>"
  }
}
```

Rules:

* Header names must be ASCII letters, digits, or standard header punctuation.
* Values must be plain strings.
* **Reserved — rejected:**
  * Envelope-routing headers (`To`, `From`, `Cc`, `Bcc`, `Reply-To`, `Subject`, `Date`, `Message-ID`, `Content-Type`, `Content-Transfer-Encoding`, `MIME-Version`). Use the dedicated parameters instead.
  * Any header in the reserved `X-Agentheya-*` namespace.

On rejection: `{ error, details: { rejected_headers: ["..."], hint } }`.

## Scheduling

Pick exactly one of:

* `scheduled_at: "2026-08-05T11:52:01.858Z"` — absolute UTC timestamp. Must be in the future.
* `send_in: "5m"` — relative delay. Supported forms: `30s`, `5m`, `1h`, `2d`, `1w` (and the longer aliases `sec/secs`, `min/mins`, `hr/hrs`, `day/days`, `wk/wks`).

The resolved absolute timestamp is returned in `scheduled_at`. On success the tool returns `status: "scheduled"`. Scheduled emails go out at the resolved time even if the conversation has long ended.

```json theme={null}
{ "scheduled_at": "2026-05-20T09:00:00.000Z" }
```

```json theme={null}
{ "send_in": "1h" }
```

## Attachments

Each entry attaches one file. The combined payload (all attachments plus body) cannot exceed **40 MB**.

| Property       | Type              | Required                  | Description                                                                          |
| -------------- | ----------------- | ------------------------- | ------------------------------------------------------------------------------------ |
| `filename`     | `string`          | yes                       | Display name (e.g. `invoice.pdf`).                                                   |
| `content`      | `string` (base64) | one of `content` / `path` | Raw file bytes, base64-encoded. Use for files generated this turn.                   |
| `path`         | `string` (URL)    | one of `content` / `path` | Public HTTPS URL pointing at the hosted file.                                        |
| `content_type` | `string`          | no                        | MIME type (e.g. `image/png`, `application/pdf`). Derived from `filename` if omitted. |
| `content_id`   | `string`          | no                        | Custom CID for inline-image mapping — reference from HTML as `cid:THAT_ID`.          |

Exactly one of `content` and `path` must be set per item — setting both, or neither, is rejected.

Example (one local + one remote + one inline image):

```json theme={null}
{
  "attachments": [
    {
      "filename": "report.pdf",
      "content": "JVBERi0xLjQKJ...==",
      "content_type": "application/pdf"
    },
    {
      "filename": "terms.pdf",
      "path": "https://example.com/legal/terms.pdf"
    },
    {
      "filename": "logo.png",
      "path": "https://cdn.example.com/logo.png",
      "content_type": "image/png",
      "content_id": "company-logo"
    }
  ],
  "body_html": "<p>See attached. <img src=\"cid:company-logo\" alt=\"logo\"></p>"
}
```

## Replying to a received email

Set `in_reply_to_inbound_id` to the id of an email this agent received (visible on the inbound row in the **Emails** dashboard). When set:

* The standard `In-Reply-To` and `References` headers are filled in automatically from the original message, so the recipient's mail client threads the new message under the original conversation.
* An authoritative reply linkage is recorded — the **Emails** dashboard shows the original inbound and the outgoing reply in each other's "Same thread" panel even when the subject line drifts away from `Re: …`.

Scoping:

* The id must belong to an email this agent received. Cross-agent ids return a structured error.
* If a subscriber is calling, the id must belong to an email the agent received from that same subscriber.

If the caller already passes `In-Reply-To` or `References` in `headers`, those values win over the auto-fill.

```json theme={null}
{
  "to": "alice@example.com",
  "subject": "Re: Your order #4821",
  "body_text": "Thanks for getting back to us — your refund is on the way.",
  "in_reply_to_inbound_id": "ah-7fk29x-m4p8qrst3wxy"
}
```

## Returns

On immediate-send success:

```json theme={null}
{
  "ok": true,
  "message_id": "...",
  "status": "queued",
  "to": "...",
  "cc": ["..."],
  "bcc": ["..."],
  "from": "<agent_name> <noreply@...>",
  "subject": "..."
}
```

On scheduled-send success:

```json theme={null}
{
  "ok": true,
  "message_id": "...",
  "status": "scheduled",
  "scheduled_at": "2026-08-05T11:52:01.858Z",
  "to": "...",
  "from": "<agent_name> <noreply@...>",
  "subject": "..."
}
```

`message_id` is the handle for `check_email_status` — store it if you'll need to look up delivery state later.

On rejection by the allowlist:

```json theme={null}
{
  "error": "One or more recipients are not on this agent's allowlist.",
  "details": {
    "rejected_to": [],
    "rejected_cc": ["stranger@example.com"],
    "rejected_bcc": [],
    "owner_email_allowed": true,
    "power_user_email_allowed": true,
    "extra_allowlist_entries": ["@acme.com", "support@example.com"],
    "hint": "..."
  }
}
```

On failure: `{ error, details: { hint, ... } }`.

## Call from your own custom tool

```js theme={null}
export async function handle(input, ctx) {
  return await ctx.tools.send_email({
    to: input.recipient,
    cc: input.cc,
    bcc: input.bcc,
    subject: `Re: ${input.topic}`,
    body_text: input.message,
    reply_to: input.reply_to,
    headers: { 'X-Campaign': 'spring-2026' },
    send_in: '1h',
    attachments: [
      { filename: 'receipt.pdf', content: input.receipt_base64, content_type: 'application/pdf' },
    ],
  });
}
```

```python theme={null}
async def handle(input, ctx):
    return await ctx.tools.send_email({
        'to': input['recipient'],
        'cc': input.get('cc'),
        'bcc': input.get('bcc'),
        'subject': f"Re: {input['topic']}",
        'body_text': input['message'],
        'reply_to': input.get('reply_to'),
        'headers': {'X-Campaign': 'spring-2026'},
        'scheduled_at': '2026-08-05T11:52:01.858Z',
        'attachments': [
            {
                'filename': 'receipt.pdf',
                'content': input['receipt_base64'],
                'content_type': 'application/pdf',
            },
        ],
    })
```

## Notes

* The `From` address is fixed to `<agent_name> <noreply@...>` and cannot be overridden.
* A scheduled email will still be sent unless explicitly cancelled — even if the conversation ends. Gate destructive flows behind `confirm_action` + `commit_action`.
* Final delivery status (delivered, bounced, complained) appears in the agent's activity feed; it is not returned synchronously by this call.
