# send_site_email

Source: https://modulify.ai/docs/api/emails/send-site-email

Sends one real email from a site's own sending address, right now, to the recipients you named.

- Title: Send an email from a site
- Scope: `config:write`
- Access: Makes changes
- Endpoint: `POST /v1/send_site_email`

The tool tells the client to use it only when you explicitly ask for a specific email to be sent to recipients you named, never to pick or look up recipients itself, never to send bulk, marketing or newsletter mail, and never to send the same message twice for one request. Pass an `idempotencyKey` when a retry could repeat the send: the same key with the same content never sends twice, and the answer then carries `duplicate: true`.

The From address is always the site's own sending address, `fromAddress` in `get_site_email_settings`, on its chosen email domain or on its built-in sending domain, and no message can pick another one. Should the chosen domain turn out not to be verified as the message goes out, it is sent once from the built-in address instead. An invalid or oversized input is refused with an error, never shortened.

The message counts against the site's monthly allowance exactly like one its code sends: every recipient is one send, and at most 60 sends go out a minute. Addresses on the site's blocked list are skipped and returned as suppressed. Its history row reads **MCP client** under **Sent by**, or **API** when it was sent through the [HTTP API](https://modulify.ai/docs/api).

The answer carries the `id`, the `accepted` and `suppressed` recipients and the `remaining` allowance, or `code` and the reason it was refused: sending is off, the site's own `EMAIL_URL` or `EMAIL_PRIVATE_KEY` secret keeps its email off, the allowance is used up, the per minute limit is reached, an address is invalid (listed in `invalid`), every recipient is blocked, or the mail provider refused it. A successful send only means the provider accepted it, so read `get_site_email` with the `id` to see whether it was delivered. [Email HTTP API](https://modulify.ai/docs/automations/email-http-api) lists every refusal code. See [What counts as a send](https://modulify.ai/docs/automations/emails#what-counts-as-a-send).

## Request

Call it with a `POST` to `https://api.modulify.ai/v1/send_site_email`, sending the inputs below as a JSON object. The token needs the `config:write` scope.

It makes changes, so send an [Idempotency-Key](https://modulify.ai/docs/api/idempotency) header whenever you might retry it. A retry with the same key gets the first answer back instead of running again.

```bash
curl -X POST https://api.modulify.ai/v1/send_site_email \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"projectId":"PROJECT_ID","to":[],"subject":"SUBJECT"}'
```

Over MCP, the same method is the [send_site_email tool](https://modulify.ai/docs/mcp/emails/send-site-email).

## Inputs

| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `projectId` | string | Yes | The site id. |
| `to` | array of strings | Yes | The recipients, as email addresses, where `Name <address>` works too. `to`, `cc` and `bcc` together take at most 50 recipients. |
| `cc` | array of strings | No | Recipients copied on the message. They count toward the same 50 recipients. |
| `bcc` | array of strings | No | Recipients copied without the others seeing them. They count toward the same 50 recipients. |
| `subject` | string | Yes | The subject line, at most 300 characters, with no line breaks. |
| `html` | string | No | The HTML body, at most 400,000 characters, with no script tag. At least one of `html` and `text` is required. |
| `text` | string | No | The plain text body, at most 400,000 characters, best sent alongside `html`. |
| `replyTo` | string | No | The one address replies should go to, a mailbox somebody reads. |
| `fromName` | string | No | The sender name for this one message, instead of the site's own sender name, at most 64 characters. |
| `tags` | array of objects | No | Labels stored with the message, at most 10, each with a `name` and a `value` of letters, digits, dashes and underscores. `project`, `send` and `forward` are reserved names. |
| `idempotencyKey` | string | No | A unique key for this message, such as an order id, so a retry never sends it twice. At most 256 characters. |

## Response

Every call answers with the [JSON envelope](https://modulify.ai/docs/api/requests-and-responses#the-response) of `success`, `message`, `data`, `code` and `version`. `data` holds the result described above, and on a method that returns a total, `count` carries it. The [response headers](https://modulify.ai/docs/api/requests-and-responses#headers-on-every-method-call) carry the call's `X-Request-Id` and what is left of your per-minute budget in `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`. [Errors](https://modulify.ai/docs/api/errors) explains every status code a call can answer with.