# Dynamic Document API: Email

> Email renders through your own provider account (Brevo, SMTP, Postmark, Resend, Amazon SES or Microsoft 365): connections, rules per template, previews, tests, sends, delivery events and resends. Growth plan and up. While email delivery isn't enabled for a workspace yet, writes to its connections and rules answer `403 feature_not_enabled`.

> Part of the Dynamic Document API documentation. Index of all pages for agents: [llms.txt](https://dynamicdocumentapi.com/llms.txt), every page in one file: [llms-full.txt](https://dynamicdocumentapi.com/llms-full.txt).

19 endpoints of the [API reference](https://dynamicdocumentapi.com/docs/api-reference/index.md), version 1.0.0. Interactive reference: https://dynamicdocumentapi.com/docs/api-reference#tag/email. Machine-readable OpenAPI document: https://dynamicdocumentapi.com/openapi/openapi-public.json

## Servers

- https://api-eu.dynamicdocumentapi.com — EU (Germany)

## Endpoints

- `GET /v1/email-connections` — List email connections
- `POST /v1/email-connections` — Create an email connection
- `GET /v1/email-connections/{connection_id}` — Retrieve an email connection
- `PATCH /v1/email-connections/{connection_id}` — Update an email connection
- `DELETE /v1/email-connections/{connection_id}` — Delete an email connection
- `POST /v1/email-connections/{connection_id}/test` — Test an email connection
- `POST /v1/email-rules/preview` — Preview a rule that is not saved yet
- `GET /v1/email-rules/{rule_id}` — Retrieve an email rule
- `PATCH /v1/email-rules/{rule_id}` — Update an email rule
- `DELETE /v1/email-rules/{rule_id}` — Delete an email rule
- `POST /v1/email-rules/{rule_id}/preview` — Preview an email rule
- `POST /v1/email-rules/{rule_id}/test` — Send a test email
- `GET /v1/email-sends` — List email sends
- `GET /v1/email-sends/stats` — Email statistics
- `GET /v1/email-sends/{send_id}` — Retrieve an email send
- `GET /v1/email-sends/{send_id}/events` — List a send's delivery events
- `POST /v1/email-sends/{send_id}/resend` — Resend an email
- `GET /v1/templates/{template_id}/email-rules` — List a template's email rules
- `POST /v1/templates/{template_id}/email-rules` — Create an email rule

## GET /v1/email-connections

**List email connections**

The workspace's email provider accounts. Credentials are never returned; `egress_ips` are the addresses to authorize at the provider.

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## POST /v1/email-connections

**Create an email connection**

Connects your own provider account. `provider` and what it takes:

* `brevo`: `credentials.api_key`, an API key (Brevo's SMTP key is not accepted).
* `smtp`: `config` `host`, `port` (587 or 2525, with STARTTLS) and `username`; `credentials.password`.
* `postmark`: `config.message_stream` (default `outbound`); `credentials.server_token`.
* `resend`: `credentials.api_key`.
* `ses` (Amazon SES): `config.region` and optionally `config.configuration_set`; `credentials` `access_key_id` and `secret_access_key`.
* `graph` (Microsoft 365): `config` `tenant_id`, `client_id` and `sender`, the mailbox that sends; `credentials.client_secret` of an app with the `Mail.Send` application permission.

Every provider takes `config.attachment_budget_bytes` and `config.max_per_second`; reads show them with the provider's defaults filled in. At most 10 connections per workspace; `provider` can't change later. `credentials` are encrypted and never returned. Authorize `egress_ips` at providers that restrict calling IPs (Brevo: Security → Authorized IPs).

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `Idempotency-Key` | header | string | Replays the stored response for 24 h; a different body with the same key → 422. |

Request body (`application/json`, required): EmailConnectionWrite

| Field | Type | Description |
|---|---|---|
| `config` | object | Per provider: SMTP `{host, port, username}` (port 587 or 2525); Postmark `{message_stream}` (default `outbound`, also when empty or null); Amazon SES `{region, configuration_set?}`; Microsoft 365 `{tenant_id, client_id, sender}` (`sender`: the mailbox that sends); Brevo and Resend none. Every provider: `attachment_budget_bytes`, the bytes of files attached per email (default / maximum: Brevo 10 / 13 MiB, SMTP 6 / 25 MiB, Postmark 6 / 6 MiB, Resend 10 / 25 MiB, Amazon SES 10 / 25 MiB, Microsoft 365 2 / 2 MiB), and `max_per_second` (1 to 100, default 10). |
| `credentials` | object | Write-only: Brevo `{api_key}` (an API key, not its SMTP key), SMTP `{password}`, Postmark `{server_token}`, Resend `{api_key}`, Amazon SES `{access_key_id, secret_access_key}`, Microsoft 365 `{client_secret}`. A PATCH without them keeps them. |
| `default_from` (required) | EmailDefaultFromWrite |  |
| `name` (required) | string |  |
| `provider` (required) | "brevo" \| "smtp" \| "postmark" \| "resend" \| "ses" \| "graph" | `brevo`, `smtp`, `postmark`, `resend`, `ses` (Amazon SES) or `graph` (Microsoft 365); it can't change after creation. * `brevo` - Brevo * `smtp` - SMTP * `postmark` - Postmark * `resend` - Resend * `ses` - Amazon SES * `graph` - Microsoft 365 (Graph) |
| `reply_to` | string \| null |  |
| `test_mode` | "sandbox" \| "redirect" \| "skip" | Test renders: `sandbox` (Brevo only, and its default: Brevo checks the email without delivering it), `redirect` (to `test_recipient`, subject prefixed `[Test] `), `skip` (no provider call; the default for every other provider). * `sandbox` - Sandbox * `redirect` - Redirect * `skip` - Skip |
| `test_recipient` | string \| null | The verified address of a workspace member; required with `test_mode: redirect`. |

Responses:

- `201` 
- `400` Invalid request body (`errors[]` with JSON pointers).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified; or `feature_not_enabled`: email delivery isn't enabled for this workspace yet.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## GET /v1/email-connections/{connection_id}

**Retrieve an email connection**

Common base: API key or OAuth only, never a dashboard region token.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `connection_id` (required) | path | string | Email connection id (`emc_…`). |

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email connection not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## PATCH /v1/email-connections/{connection_id}

**Update an email connection**

Partial update; a body without `credentials` keeps them. A `config` replaces the stored one as a whole. `provider` is immutable. Stored credentials are only sent to the server they were entered for: an SMTP `config` that changes `host`, `port` or `username` needs `credentials` in the same request, otherwise the request is refused (400, `/credentials`).

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `connection_id` (required) | path | string | Email connection id (`emc_…`). |

Request body (`application/json`): PatchedEmailConnectionWrite

| Field | Type | Description |
|---|---|---|
| `config` | object | Per provider: SMTP `{host, port, username}` (port 587 or 2525); Postmark `{message_stream}` (default `outbound`, also when empty or null); Amazon SES `{region, configuration_set?}`; Microsoft 365 `{tenant_id, client_id, sender}` (`sender`: the mailbox that sends); Brevo and Resend none. Every provider: `attachment_budget_bytes`, the bytes of files attached per email (default / maximum: Brevo 10 / 13 MiB, SMTP 6 / 25 MiB, Postmark 6 / 6 MiB, Resend 10 / 25 MiB, Amazon SES 10 / 25 MiB, Microsoft 365 2 / 2 MiB), and `max_per_second` (1 to 100, default 10). |
| `credentials` | object | Write-only: Brevo `{api_key}` (an API key, not its SMTP key), SMTP `{password}`, Postmark `{server_token}`, Resend `{api_key}`, Amazon SES `{access_key_id, secret_access_key}`, Microsoft 365 `{client_secret}`. A PATCH without them keeps them. |
| `default_from` | EmailDefaultFromWrite |  |
| `name` | string |  |
| `provider` | "brevo" \| "smtp" \| "postmark" \| "resend" \| "ses" \| "graph" | `brevo`, `smtp`, `postmark`, `resend`, `ses` (Amazon SES) or `graph` (Microsoft 365); it can't change after creation. * `brevo` - Brevo * `smtp` - SMTP * `postmark` - Postmark * `resend` - Resend * `ses` - Amazon SES * `graph` - Microsoft 365 (Graph) |
| `reply_to` | string \| null |  |
| `test_mode` | "sandbox" \| "redirect" \| "skip" | Test renders: `sandbox` (Brevo only, and its default: Brevo checks the email without delivering it), `redirect` (to `test_recipient`, subject prefixed `[Test] `), `skip` (no provider call; the default for every other provider). * `sandbox` - Sandbox * `redirect` - Redirect * `skip` - Skip |
| `test_recipient` | string \| null | The verified address of a workspace member; required with `test_mode: redirect`. |

Responses:

- `200` 
- `400` Invalid request body (`errors[]` with JSON pointers).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified; or `feature_not_enabled`: email delivery isn't enabled for this workspace yet.
- `404` Email connection not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## DELETE /v1/email-connections/{connection_id}

**Delete an email connection**

A connection that rules use answers `409 email_connection_in_use`: delete or move those rules first. Works on every plan.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `connection_id` (required) | path | string | Email connection id (`emc_…`). |

Responses:

- `204` Deleted.
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email connection not found.
- `409` Rules use the connection (`email_connection_in_use`).
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## POST /v1/email-connections/{connection_id}/test

**Test an email connection**

Checks the saved settings from the region that sends, step by step:

* `brevo`: `credentials`, `senders` (the verified senders and domains) and `sandbox_send` (Brevo checks an email from the default sender without delivering it).
* `smtp`: `connect`, `starttls`, `smtp_auth`.
* `postmark`: `credentials`.
* `resend`: `credentials`, which also lists the verified domains.
* `ses`: `credentials`, `senders` (the verified identities; it fails when there are none).
* `graph`: `credentials` (Microsoft Entra ID issues a token).

The senders found are stored as the connection's `senders`. `send_to` adds a real test email (`send`) to a verified member address. `200` with `ok: false` means a step failed; the test stops there. A failed `credentials` or `smtp_auth` step marks the connection `failing`.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `Idempotency-Key` | header | string | Replays the stored response for 24 h; a different body with the same key → 422. |
| `connection_id` (required) | path | string | Email connection id (`emc_…`). |

Request body (`application/json`): EmailConnectionTestRequest

| Field | Type | Description |
|---|---|---|
| `send_to` | string \| null | Also send a real test email to this address: the verified address of a workspace member. |

Responses:

- `200` The test ran; check `ok`.
- `400` Invalid request body (`errors[]` with JSON pointers).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified; or `feature_not_enabled`: email delivery isn't enabled for this workspace yet.
- `404` Email connection not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## POST /v1/email-rules/preview

**Preview a rule that is not saved yet**

A watermarked preview render of the template (its live version, else its draft) with the rule's email rendered in the document's context: the fields, the recipients as parsed and the attachments as they would be planned. Nothing is sent and nothing is billed. The data is `data`, else the dataset `dataset_id`, else the template's default dataset, else `{}`. `rule.connection_id` names the connection.

Request body (`application/json`): EmailRuleDraftPreviewRequest

| Field | Type | Description |
|---|---|---|
| `data` | object or null |  |
| `dataset_id` | string or null |  |
| `rule` (required) | EmailRuleSpec | The editable fields of an email rule, as the rule editor holds them (unsaved state). Used instead of the saved rule; fields the editor does not know (`name`, `enabled`, …) are ignored. `null` counts as empty. Syntax errors come back as the preview's `email_preview.error`. |
| `template_id` (required) | string |  |

Responses:

- `200` 
- `202` The preview did not finish within the sync wait (`email_preview: null`).
- `400` Invalid request (`errors[]`), e.g. an unknown `rule.connection_id`.
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Rule, template, connection or dataset not found.
- `410` Template deleted.
- `422` The document failed to render (`render_error`).
- `429` Rate limited (`Retry-After`).
- `503` Region degraded or the email checks are unavailable (`Retry-After`).

## GET /v1/email-rules/{rule_id}

**Retrieve an email rule**

Common base: API key or OAuth only, never a dashboard region token.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `rule_id` (required) | path | string | Email rule id (`emr_…`). |

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email rule not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## PATCH /v1/email-rules/{rule_id}

**Update an email rule**

Partial update; changing `position` moves the others. Switching a rule off (`enabled: false`) also cancels its emails that were not sent yet. Below the Growth plan, or while email delivery isn't enabled for the workspace, an update may only switch the rule off.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `rule_id` (required) | path | string | Email rule id (`emr_…`). |

Request body (`application/json`): PatchedEmailRuleWrite

| Field | Type | Description |
|---|---|---|
| `attach_formats` | array of "pdf" \| "png" \| "jpeg" \| "webp" \| "html" \| "xml" \| "zip" | Only files of these formats; empty = every file. |
| `attachments` | "attach_or_link" \| "attach" \| "link" \| "none" | * `attach_or_link` - Attach Or Link * `attach` - Attach * `link` - Link * `none` - None |
| `bcc` | string \| null |  |
| `cc` | string \| null |  |
| `condition` | string \| null | An expression; the rule sends when it is true. Empty = always. |
| `connection_id` | string | `emc_…` of this workspace. |
| `enabled` | boolean |  |
| `from_email` | string \| null | Literal; empty = the connection's default sender. One the provider hasn't verified is saved with the warning `email_sender_unverified`. |
| `from_name` | string \| null | Template source; one line after rendering. |
| `html` | string \| null | Template source, values HTML-escaped (≤ 256 KiB). |
| `name` | string |  |
| `position` | integer | 0 to 4; the others move. Default: the end. |
| `provider_template` | EmailProviderTemplate or null |  |
| `reply_to` | string \| null | Template source; one line after rendering. |
| `subject` | string \| null | Template source; required unless a provider template gives it. |
| `text` | string \| null | Template source; empty = derived from `html`. |
| `to` | string \| null | Template source rendering to addresses separated by `,` `;` or line breaks. |

Responses:

- `200` 
- `400` Invalid request body (`errors[]` with JSON pointers).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified; or `feature_not_enabled`: email delivery isn't enabled for this workspace yet.
- `404` Email rule not found.
- `422` A field does not compile (`email_template_error`).
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## DELETE /v1/email-rules/{rule_id}

**Delete an email rule**

Its emails that were not sent yet are canceled. Works on every plan.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `rule_id` (required) | path | string | Email rule id (`emr_…`). |

Responses:

- `204` Deleted.
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email rule not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## POST /v1/email-rules/{rule_id}/preview

**Preview an email rule**

A watermarked preview render of the template (its live version, else its draft) with the rule's email rendered in the document's context: the fields, the recipients as parsed and the attachments as they would be planned. Nothing is sent and nothing is billed. The data is `data`, else the dataset `dataset_id`, else the template's default dataset, else `{}`. Works for switched-off rules; `rule` previews unsaved fields instead of the saved ones.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `rule_id` (required) | path | string | Email rule id (`emr_…`). |

Request body (`application/json`): EmailRulePreviewRequest

| Field | Type | Description |
|---|---|---|
| `data` | object or null |  |
| `dataset_id` | string or null | A dataset (`ds_…`) of the template. |
| `rule` | EmailRuleSpec or null | Unsaved fields instead of the saved rule. |

Responses:

- `200` 
- `202` The preview did not finish within the sync wait (`email_preview: null`).
- `400` Invalid request (`errors[]`), e.g. an unknown `rule.connection_id`.
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Rule, template, connection or dataset not found.
- `410` Template deleted.
- `422` The document failed to render (`render_error`).
- `429` Rate limited (`Retry-After`).
- `503` Region degraded or the email checks are unavailable (`Retry-After`).

## POST /v1/email-rules/{rule_id}/test

**Send a test email**

Renders the template (its live version, else its draft) as a test render (free, watermarked) whose one email is this rule's, redirected to `to` with the subject prefixed `[Test] `. `to` must be the verified address of an active member of the workspace. Works for switched-off rules; `rule` tests unsaved fields. Follow the render: its `email[]` shows the send.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `Idempotency-Key` | header | string | Replays the stored response for 24 h; a different body with the same key → 422. |
| `rule_id` (required) | path | string | Email rule id (`emr_…`). |

Request body (`application/json`): EmailRuleTestRequest

| Field | Type | Description |
|---|---|---|
| `data` | object or null |  |
| `dataset_id` | string or null |  |
| `rule` | EmailRuleSpec or null | Unsaved fields instead of the saved rule. |
| `to` (required) | string | The verified email address of an active member of the workspace. |

Responses:

- `202` The test render was queued (`Location: /v1/renders/{id}`).
- `400` Invalid request, or `to` is not a verified member address (`/to`).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Rule, template, connection or dataset not found.
- `410` Template deleted.
- `422` Zero-retention workspaces keep no file to email (`email_needs_hosted_file`).
- `429` Rate limited (`Retry-After`).
- `503` Region degraded (`Retry-After`).

## GET /v1/email-sends

**List email sends**

Newest first; sends are kept for 30 days. Test keys see test sends only.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `batch_id` | query | string |  |
| `connection_id` | query | string |  |
| `created_after` | query | string |  |
| `cursor` | query | string |  |
| `limit` | query | integer | 1 to 100 (default 50). |
| `render_id` | query | string |  |
| `rule_id` | query | string |  |
| `status` | query | "canceled" \| "failed" \| "pending" \| "retrying" \| "sandboxed" \| "sending" \| "sent" \| "skipped" \| "unknown" |  |
| `template_id` | query | string |  |

Responses:

- `200` 
- `400` Malformed id, unknown `status` or invalid `created_after`.
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `429` Rate limited (`Retry-After`).

## GET /v1/email-sends/stats

**Email statistics**

Today's recipients of live emails in this region against the daily cap (it resets at 00:00 UTC), and the sends of the last 30 days by status.

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `429` Rate limited (`Retry-After`).
- `503` Region degraded (`Retry-After`).

## GET /v1/email-sends/{send_id}

**Retrieve an email send**

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `send_id` (required) | path | string | Email send id (`ems_…`). |

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email send not found.
- `429` Rate limited (`Retry-After`).

## GET /v1/email-sends/{send_id}/events

**List a send's delivery events**

What Brevo reported after it accepted the email (delivered, deferred, bounces, blocks, spam complaints, invalid addresses), oldest first. Only Brevo connections have delivery events: other providers' sends list none. Also sent as the webhooks `email.delivered`, `email.bounced` and `email.complained`.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `send_id` (required) | path | string | Email send id (`ems_…`). |

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email send not found.
- `429` Rate limited (`Retry-After`).

## POST /v1/email-sends/{send_id}/resend

**Resend an email**

Sends a `failed`, `unknown` or `sent` email again as a new send (`resend_of` set) with the same content and files; it counts toward today's cap. An email that failed before it was sent (template, recipients or attachments) or whose content was not kept (request logging off) answers `409 email_not_resendable`; a file that is no longer hosted `409 email_file_expired`.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `Idempotency-Key` | header | string | Replays the stored response for 24 h; a different body with the same key → 422. |
| `send_id` (required) | path | string | Email send id (`ems_…`). |

Responses:

- `201` 
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Email send not found.
- `409` `email_not_resendable` or `email_file_expired`.
- `429` Rate limited (`Retry-After`).
- `503` Region degraded (`Retry-After`).

## GET /v1/templates/{template_id}/email-rules

**List a template's email rules**

0 to 5 rules in `position` order. Rules are live configuration, not versioned with the template: an edit applies to renders submitted afterwards.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `template_id` (required) | path | string | Template id (`tpl_…`). |

Responses:

- `200` 
- `401` Missing, invalid, expired or revoked credentials.
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified.
- `404` Template not found.
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).

## POST /v1/templates/{template_id}/email-rules

**Create an email rule**

`from_name`, `reply_to`, `to`, `cc`, `bcc`, `subject`, `html` and `text` use the template language with the document's context (data keys, `data`, `render` including `render.invoice`) plus `files` and `download_links`; `condition` is an expression. Every field must compile, else `422 email_template_error` with `errors[].path`, line and column. A `from_email` the provider hasn't verified is saved with the warning `email_sender_unverified`.

Parameters:

| Name | In | Type | Description |
|---|---|---|---|
| `Idempotency-Key` | header | string | Replays the stored response for 24 h; a different body with the same key → 422. |
| `template_id` (required) | path | string | Template id (`tpl_…`). |

Request body (`application/json`, required): EmailRuleWrite

| Field | Type | Description |
|---|---|---|
| `attach_formats` | array of "pdf" \| "png" \| "jpeg" \| "webp" \| "html" \| "xml" \| "zip" | Only files of these formats; empty = every file. |
| `attachments` | "attach_or_link" \| "attach" \| "link" \| "none" | * `attach_or_link` - Attach Or Link * `attach` - Attach * `link` - Link * `none` - None |
| `bcc` | string \| null |  |
| `cc` | string \| null |  |
| `condition` | string \| null | An expression; the rule sends when it is true. Empty = always. |
| `connection_id` (required) | string | `emc_…` of this workspace. |
| `enabled` | boolean |  |
| `from_email` | string \| null | Literal; empty = the connection's default sender. One the provider hasn't verified is saved with the warning `email_sender_unverified`. |
| `from_name` | string \| null | Template source; one line after rendering. |
| `html` | string \| null | Template source, values HTML-escaped (≤ 256 KiB). |
| `name` (required) | string |  |
| `position` | integer | 0 to 4; the others move. Default: the end. |
| `provider_template` | EmailProviderTemplate or null |  |
| `reply_to` | string \| null | Template source; one line after rendering. |
| `subject` | string \| null | Template source; required unless a provider template gives it. |
| `text` | string \| null | Template source; empty = derived from `html`. |
| `to` | string \| null | Template source rendering to addresses separated by `,` `;` or line breaks. |

Responses:

- `201` 
- `400` Invalid request body (`errors[]` with JSON pointers).
- `401` Missing, invalid, expired or revoked credentials.
- `402` Email delivery needs the Growth plan or higher (`plan_feature_unavailable`).
- `403` Insufficient scope, IP not allowed, workspace suspended or scheduled for deletion, or email not verified; or `feature_not_enabled`: email delivery isn't enabled for this workspace yet.
- `404` Template or connection not found.
- `422` A field does not compile (`email_template_error`).
- `429` Rate limited (`Retry-After`).
- `503` The control plane is unreachable (`Retry-After`).
