# Create a draft

POST /v1/inboxes/{inbox_id}/drafts

Source: https://cherami.to/docs/api/drafts/create-draft



{/* Generated from apps/web/openapi. Edit the contract, not this file. */}

`POST /v1/inboxes/{inbox_id}/drafts`

Requires a [Claim-issued API key](https://cherami.to/docs/api#authentication): `Authorization: Bearer YOUR_CREDENTIAL`.

Drafts belong to one owned inbox. Saving or editing consumes no sending allowance and does not require sending permission. Sending requires current permission, recipient-policy approval and available allowance; deletion requires deletion permission.

The body can be `{}` for an empty draft. Supply any of `to`, `cc`, `bcc`, `subject`, `text`, `html`, `attachments`, `in_reply_to` and `labels`, using the [sending field formats and limits](https://cherami.to/docs/api/sending/send-message). Recipients, subject and text may be missing or empty until sending. Unknown fields are rejected. Creation and edit JSON may be up to 8 MiB; saved content uses the same 5 MiB local bound, 50 recipients and 32 attachments as outgoing mail. Sending also checks current limits, including the provider's generated MIME limit.

`html` and `in_reply_to` additionally accept null to clear. Empty arrays clear recipient lists, attachments or initial sent-copy labels. Recipient display names are preserved. Supplied attachments are padded base64 original bytes, not URLs.

Optional `idempotency_key` protects creation. It accepts 1–128 ASCII letters, digits, hyphens or underscores. Keep it with the original payload and first request time; follow the creation-recovery guidance on this page.

New creation returns `201`, `Location: /v1/drafts/{id}` and metadata.

The `replayed` and `idempotency_expires_at` fields appear only for keyed creation. Creation returns metadata, not the full body; retrieve the draft to inspect saved content.

### Prepare a reply or forward [#prepare-a-reply-or-forward]

Creation also accepts `source`.

`source.action` is `reply`, `reply-all` or `forward`. `message_id` must identify a ready received or accepted sent message in the same inbox. The [ordinary correspondence derivation rules](https://cherami.to/docs/api/sending/reply-message) apply: reply recipients exclude self and original Bcc; forwards retain original bodies rather than extracted reply text.

Replies save derived recipients, subject and `in_reply_to`, with your supplied response and new attachments. No original history or files are automatically copied. Explicit fields override derived fields, including empty arrays or a null reply target. The reply target must remain available and usable when the draft is sent; changing `in_reply_to` later does not rederive recipients or subject.

For forwards, `text` on creation is the introductory note. Supply recipients explicitly, or add them later. The saved text and HTML contain the full forward. `source.include_attachments` defaults to true and is valid only for forwards; false excludes all original files, including embedded images. Creation with a forward source cannot also supply `attachments`; edit afterward to replace the saved file list. Original bytes and usable inline Content-ID relationships are retained. Missing or oversized included files fail rather than being silently omitted. Forwarding does not redact private information already in the body.

Preparation happens once, before the draft is returned. Sending does not regenerate recipients, forward content or attachments from the source. A prepared forward remains usable if its source is subsequently deleted. A source is a preparation instruction on creation, not an editable field.

### Recover creation [#recover-creation]

Creation keys are account-scoped across HTTP and MCP, in a namespace separate from inbox creation and sending. Protection lasts **24 hours from creation**, without renewal. A matching retry returns `200`, `replayed: true` and the original draft's **current metadata**, including later edits or submission state. It never reapplies the creation payload. A changed payload returns `409 idempotency_conflict`.

The comparison uses normalized creation intent: omitted/empty arrays, trimmed display names, normalized label sets and missing/empty subject or text are equivalent. Content and recipient/file order remain significant. With a source, explicitly supplied fields are also significant because they override derived values; preserve the original payload. Default and explicit true attachment inclusion are equivalent.

Deleting the draft does not release an active key. A matching retry returns `409 idempotency_result_unavailable`, without creating a replacement. Replay does not require reloading a preparation source that has since disappeared. Access and inbox ownership still apply.

If creation cannot be confirmed, retry only with the original key and payload within a conservatively measured 24 hours of the first request. Without a key or after expiry, list drafts with `state=all` and reconcile before creating anything else. Unlike an inbox address, draft content is not unique: blindly recreating can allocate a duplicate. A missing entry in one page does not establish that creation failed.

## Parameters [#parameters]

| Parameter  | Location | Required | Type   | Meaning                                        |
| ---------- | -------- | -------- | ------ | ---------------------------------------------- |
| `inbox_id` | path     | Yes      | string | Owned Cherami resource ID returned by the API. |

## Request body [#request-body]

JSON object. Total UTF-8 request body limit: 8388608 bytes.

### Schema: CreateDraft [#schema-createdraft]

Incomplete content is allowed, including missing/empty recipients, subject and text. At most 50 combined recipients and 32 attachments; same local content bound as sending. With a forward source, attachments cannot also be supplied; text is the introductory note on creation only.

| Field             | Required | Type                                                        | Meaning and constraints                                                                                                                                                              |
| ----------------- | -------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `to`              | No       | array of [Mailbox](#schema-mailbox)                         | `maxItems`: `50`                                                                                                                                                                     |
| `cc`              | No       | array of [Mailbox](#schema-mailbox)                         | `maxItems`: `50`                                                                                                                                                                     |
| `bcc`             | No       | array of [Mailbox](#schema-mailbox)                         | `maxItems`: `50`                                                                                                                                                                     |
| `subject`         | No       | string                                                      | No control characters; at most 998 UTF-8 bytes. `x-max-utf8-bytes`: `998`                                                                                                            |
| `text`            | No       | string                                                      | Plain-text body.                                                                                                                                                                     |
| `html`            | No       | string or null                                              |                                                                                                                                                                                      |
| `attachments`     | No       | array of [AttachmentInput](#schema-attachmentinput) or null | Omitted or null means no attachments; an empty array also clears draft attachments.                                                                                                  |
| `in_reply_to`     | No       | string or null                                              |                                                                                                                                                                                      |
| `labels`          | No       | array of [Label](#schema-label)                             | Trimmed, deduplicated and case-sensitive. Order is not significant. `maxItems`: `32`                                                                                                 |
| `idempotency_key` | No       | string                                                      | Retain a unique key, exact payload and first request time for this intended operation. Account-scoped protection lasts 24 hours without renewal. `pattern`: `^[A-Za-z0-9_-]{1,128}$` |
| `source`          | No       | object or object                                            |                                                                                                                                                                                      |

Unknown fields are rejected.

#### `source` fields [#source-fields]

Exactly one of these shapes applies.

##### Alternative 1 [#alternative-1]

| Field        | Required | Type                       | Meaning and constraints                                                                                                      |
| ------------ | -------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `action`     | Yes      | `"reply"` or `"reply-all"` |                                                                                                                              |
| `message_id` | Yes      | string                     | Received or accepted sent resource ID in this inbox. The source must have usable reply headers. `pattern`: `^[a-f0-9-]{36}$` |

Unknown fields are rejected.

##### Alternative 2 [#alternative-2]

| Field                 | Required | Type        | Meaning and constraints                                                                                                      |
| --------------------- | -------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `action`              | Yes      | `"forward"` |                                                                                                                              |
| `message_id`          | Yes      | string      | Received or accepted sent resource ID in this inbox. The source must have usable reply headers. `pattern`: `^[a-f0-9-]{36}$` |
| `include_attachments` | No       | boolean     | `default`: `true`                                                                                                            |

Unknown fields are rejected.

## curl example [#curl-example]

Replace resource-ID placeholders with returned IDs. Supply `CHERAMI_API_KEY` through your private shell environment.

Save the intended payload as a private `request.json` file and replace illustrative values. For an uncertain response, follow the [operation-specific recovery rules](https://cherami.to/docs/api/errors#retry-by-operation).

```json
{
  "subject": "Proposal for review",
  "text": "Draft proposal.",
  "idempotency_key": "RETAIN_A_UNIQUE_CREATION_KEY"
}
```

```sh
curl --silent --show-error --include --request POST \
  "https://cherami.to/v1/inboxes/INBOX_ID/drafts" \
  --header "Authorization: Bearer $CHERAMI_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json
```

## Responses [#responses]

### HTTP 200 [#http-200]

Matching replay: current resource or saved attempt, without another allocation or provider submission.

* `X-Request-ID`: Support correlation ID, not an idempotency key.
* `Location`: Relative URL of the resulting resource.

Content type: `application/json`.

All of these schemas apply.

### Part 1 [#part-1]

[CreatedDraft](#schema-createddraft)

### Part 2 [#part-2]

| Field                    | Required | Type   | Meaning and constraints                                                                                                              |
| ------------------------ | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `replayed`               | Yes      | `true` |                                                                                                                                      |
| `idempotency_expires_at` | Yes      | string | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$` |

```json
{
  "id": "11111111-1111-4111-8111-111111111111",
  "inbox_id": "11111111-1111-4111-8111-111111111111",
  "state": "draft",
  "subject": "Example",
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-01T00:00:00.000Z",
  "sent_message_id": null,
  "replayed": true,
  "idempotency_expires_at": "2026-10-02T00:00:00.000Z"
}
```

### HTTP 201 [#http-201]

Successful operation; inspect resource state and outcome fields.

* `X-Request-ID`: Support correlation ID, not an idempotency key.
* `Location`: Relative URL of the resulting resource.

Content type: `application/json`.

All of these schemas apply.

### Part 1 [#part-1-1]

[CreatedDraft](#schema-createddraft)

### Part 2 [#part-2-1]

| Field      | Required | Type    | Meaning and constraints |
| ---------- | -------- | ------- | ----------------------- |
| `replayed` | No       | `false` |                         |

### HTTP 400 [#http-400]

`invalid_json`: Send a valid UTF-8 JSON object, not an array or scalar.

`invalid_draft`: Use supported draft fields, source preparation or listing parameters. Draft cursors that are malformed or do not match the inbox/state also use this code.

`invalid_message`: Correct the send fields, recipients, reply ID, or attachments using the message's guidance.

`invalid_name`: Correct the inbox, sender or recipient display name using the returned guidance.

`invalid_labels`: Correct label names, changes, filter groups, or discovery prefix.

`invalid_idempotency_key`: Use 1–128 ASCII letters, digits, hyphens or underscores.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 401 [#http-401]

`unauthorized`: Provide a valid bearer credential. Use [human-approved recovery](https://cherami.to/docs/guides/recovery) if access is lost.

* `X-Request-ID`: Support correlation ID, not an idempotency key.
* `WWW-Authenticate`: `"Bearer"`

Content type: `application/json`.

[Error](#schema-error)

### HTTP 404 [#http-404]

`not_found`: Resource is absent or inaccessible to this account. Reply targets must be in the sending inbox.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 409 [#http-409]

`idempotency_conflict`: The key belongs to different input. Recover with the original inbox and payload, not a replacement key.

`idempotency_result_unavailable`: The key was used but its inbox, draft or sent copy is unavailable. No replacement was created or submitted; do not bypass protection with a new key.

`reply_not_ready`: A received reply or forward source must be ready; a sent source must have confirmed provider acceptance.

`reply_recipients_unavailable`: No other visible reply recipients remain after self-exclusion. Use explicit send with human-authorized recipients.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 413 [#http-413]

`body_too_large`: Reduce the JSON request to the endpoint's body limit.

`message_too_large`: Reduce message content and attachments. Passing local checks does not guarantee generated MIME fits the provider limit.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 415 [#http-415]

`unsupported_media_type`: Send `Content-Type: application/json`.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

### HTTP 503 [#http-503]

`content_unavailable`: Expected stored content is unavailable. Retry the read later.

`draft_unavailable`: Draft operation is uncertain. Follow [draft-specific recovery](https://cherami.to/docs/api/drafts); do not blindly create a replacement.

* `X-Request-ID`: Support correlation ID, not an idempotency key.

Content type: `application/json`.

[Error](#schema-error)

## Schema: CreatedDraft [#schema-createddraft]

| Field                    | Required | Type                       | Meaning and constraints                                                                                                              |
| ------------------------ | -------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                     | Yes      | string                     | Cherami resource ID, distinct from the RFC Message-ID. Use the returned value.                                                       |
| `inbox_id`               | Yes      | string                     | Cherami resource ID, distinct from the RFC Message-ID. Use the returned value.                                                       |
| `state`                  | Yes      | `"draft"` or `"submitted"` |                                                                                                                                      |
| `subject`                | Yes      | string                     |                                                                                                                                      |
| `created_at`             | Yes      | string                     | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$` |
| `updated_at`             | Yes      | string                     | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$` |
| `sent_message_id`        | Yes      | string or null             |                                                                                                                                      |
| `replayed`               | No       | boolean                    |                                                                                                                                      |
| `idempotency_expires_at` | No       | string                     | UTC service instant with milliseconds and Z suffix. `format`: `date-time` `pattern`: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$` |

## Schema: Error [#schema-error]

| Field   | Required | Type   | Meaning and constraints |
| ------- | -------- | ------ | ----------------------- |
| `error` | Yes      | object |                         |

### `error` fields [#error-fields]

| Field     | Required | Type   | Meaning and constraints                                                                       |
| --------- | -------- | ------ | --------------------------------------------------------------------------------------------- |
| `code`    | Yes      | string | Programmatic error code. Handle unrecognized codes by status and operation-specific recovery. |
| `message` | Yes      | string | Human-readable context, not a stable string to match.                                         |

## Schema: Mailbox [#schema-mailbox]

| Field     | Required | Type   | Meaning and constraints                                                                                                              |
| --------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `address` | Yes      | string | Bare ASCII address, at most 254 characters, local part at most 64. No display-name header syntax. `maxLength`: `254`                 |
| `name`    | No       | string | Unicode name, trimmed; blank means unnamed. No control characters. At most 256 UTF-8 bytes after trimming. `x-max-utf8-bytes`: `256` |

Unknown fields are rejected.

## Schema: AttachmentInput [#schema-attachmentinput]

| Field      | Required | Type   | Meaning and constraints                                                                                                                                                           |
| ---------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `filename` | Yes      | string | Nonempty, no control characters, slash or backslash. At most 255 UTF-8 bytes. `minLength`: `1` `x-max-utf8-bytes`: `255`                                                          |
| `type`     | Yes      | string | MIME type without parameters. `maxLength`: `127` `pattern`: `^[A-Za-z0-9!#$&^_.+-]+/[A-Za-z0-9!#$&^_.+-]+$`                                                                       |
| `content`  | Yes      | string | Padded base64 original bytes, no whitespace; encoded length must be a multiple of four. Empty files are accepted. `contentEncoding`: `base64` `pattern`: `^[A-Za-z0-9+/]*={0,2}$` |

Unknown fields are rejected.

## Schema: Label [#schema-label]

1–128 UTF-8 bytes after trimming. Well-formed Unicode without control characters; case-sensitive.

string. `x-max-utf8-bytes`: `128`

[HTTP conventions, errors and pagination](https://cherami.to/docs/api/errors) · [Download OpenAPI 3.1](https://cherami.to/openapi.json)
