# Sending Safely with AI

An AI can only send through Flomailr's own send path, and that path enforces its rules on the server. None of them are settings the AI passes in. The AI can ask for a send. It cannot talk its way past the checks.

There are two send tools:

- `send_email`: a personal or one-to-one message to 1 to 5 addresses. Not marketing.
- `send_campaign`: an existing campaign, sent to its list.

Both start as a dry run.

## Dry run, then confirm

Both tools default to `dryRun: true`. A dry run sends nothing and reports what would happen:

- `send_campaign` returns the audience counts (total, allowed, blocked, deferred), the blocked addresses with the reason for each, the deferred addresses with the time they can go, and how much of today's daily cap is left. Counts are exact. The address lists show up to 25 of each, so a large list does not flood the chat.
- `send_email` returns the exact From address, the subject, the size of the rendered HTML, who would receive it and who is blocked.

To send for real the AI must call again with `dryRun: false` and `confirm: true`. With `dryRun: false` and no `confirm`, nothing is sent and the result is `BLOCKED`. Show yourself the dry run before you agree to the second call.

You can force dry runs. In `Settings > Guardrails`, turn on `Agents must opt in to real sends`. While it is on, the send tools report a dry run even when the AI asks for a real send.

## What the server checks

| Check              | What it does                                                                                                                                                                                                                                                    |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Suppression list   | Addresses that hard bounced, complained, unsubscribed or were blocked by hand are skipped. `send_email` always skips them. For `send_campaign` the check is on by default and the send also drops suppressed addresses on its own. Blocked reason: `SUPPRESSED` |
| Blocked domains    | Any recipient on a domain in your never-send list is skipped. Reason: `BLOCKED_DOMAIN`                                                                                                                                                                          |
| Daily send cap     | An optional ceiling on messages per calendar day, counted in your default time zone. Recipients past the cap are blocked. Reason: `DAILY_CAP`. Both send tools respect it                                                                                       |
| Quiet hours        | An optional window, such as 21:00 to 08:00, where `send_campaign` holds messages back. It uses each contact's own time zone and falls back to your default. Those recipients are deferred, not dropped. `send_email` does not use quiet hours                   |
| Master switch      | If `Enforce sending guardrails` is off, every guarded send is blocked rather than sent unchecked                                                                                                                                                                |
| Suspension         | A suspended workspace cannot send at all                                                                                                                                                                                                                        |
| Monthly plan limit | A send that would pass your plan's monthly limit is refused                                                                                                                                                                                                     |
| Invalid addresses  | Malformed addresses are blocked (`INVALID_EMAIL`)                                                                                                                                                                                                               |

`send_campaign` also needs the campaign to have a list with subscribed contacts, and your workspace needs a business mailing address, because campaign emails carry it in the footer. It sends list-based campaigns only. A campaign aimed at a segment must be sent from the dashboard.

The AI tools sort recipients one by one, so a campaign can go out to some people while others are blocked or deferred. Sends started from the dashboard, the REST API or a schedule are stricter: a send that would pass the daily cap, or that starts inside quiet hours, is refused as a whole, and a scheduled one is retried after the window ends.

Admins set the cap, quiet hours, blocked domains and switches under `Settings > Guardrails`. Members can read them. See [Sending Health](/docs/concepts/sending-health).

## The 5-recipient cap

`send_email` accepts at most 5 recipients. This is a hard limit in the server. A request with 6 or more is refused, and so is a request with any invalid address, so nothing is silently dropped. Duplicates are removed.

`send_email` adds no tracking pixel and no marketing unsubscribe footer, because it is meant for a personal note. The message can be a designed email (the AI passes the design and Flomailr renders it), your own HTML, or plain text. If the rendered HTML is larger than 102 KB, the result warns that Gmail will clip it.

For anything bigger than five people, the AI should save a campaign and use `send_campaign`.

## Idempotency

Both tools take an `idempotencyKey`. The AI should pass a stable one on any real send.

- Calling again with the same key and the same request returns the original result, marked `REPLAYED`, and sends nothing.
- The same key with a different request is rejected.
- The same key while the first call is still running returns `BLOCKED`.

Campaign sends have a second layer. Recipients go out in batches of 100, and each accepted batch is recorded before the next one starts. If a large send is cut short, it continues in the background and finishes by itself. The result then says mode `SENDING`. The AI should not call send again, and if it does, the repeat only resumes the same send and never mails anyone twice.

## Quiet hours override

`send_campaign` has an `overrideQuietHours` option, which lets a real send ignore your quiet-hours window. It affects quiet hours only. It does not change suppression, blocked domains, the daily cap, suspension or the monthly limit. If you do not want a send to go out at night, ask your AI not to use it.

## Everything is logged

Each action goes into an audit log: dry runs, sends, direct sends, saved drafts, suppression changes, policy reads, readiness and compliance checks, performance reads and brand updates. Each entry records the tool, a plain summary, the campaign and the idempotency key.

- Owners and admins read it under `Settings > Activity`, filtered by action.
- Your AI can read it with `get_action_log`.

The log is append-only. Revoking a key or disconnecting an app does not erase what it did.

## Who can change what

When the connector is signed in over OAuth, it acts as you. Adding or removing suppression entries, and saving the brand kit, need an owner or admin role. Suppression changes also need `confirm: true`. Connected through an API key, those actions are allowed, because only admins can create keys. Guardrail settings themselves can only be edited in the dashboard, not through the connector.

## A safe order for the AI

1. `get_send_policy` to read the cap, quiet hours, blocked domains and how many sends are left today.
2. `check_send_readiness` for a go, caution or no-go on your sending domain and recent bounce and complaint rates.
3. `check_compliance` and `score_email` on the draft. Fix errors.
4. `send_campaign` with `dryRun: true`. Read the numbers and the blocked list.
5. After you agree, `send_campaign` with `dryRun: false`, `confirm: true` and an `idempotencyKey`.
6. `get_campaign_performance` afterwards to see opens, clicks, bounces and complaints.

## Related

- [Connect Your AI](/docs/ai/connect)
- [MCP Tool Reference](/docs/ai/tools)
- [Compliance](/docs/concepts/compliance)
