---
title: "Create a draft"
method: POST
path: "/v1/drafts"
tags: ["Drafts"]
---

# Create a draft

`POST /v1/drafts`

## Request

- Base URL: `https://api.mails.ai`
- URL: `https://api.mails.ai/v1/drafts`
- Auth: HTTP bearer

**Requires scope:** `send` or `draft`. Creates a draft, or a scheduled draft if `send_at` is in the future. Scheduling sends the draft at that time, so a `send_at` needs the `send` scope: a key with only `draft` gets 403 `insufficient_scope`. A draft cannot carry `attachments` (422 `invalid_field`, param `attachments`); send files with POST /v1/messages.

## Request body

- CreateDraftRequest
  - `agent` string: Which agent sends the draft: its name or `agt_` id. Left out: the agent a key is tied to, else the workspace's only agent, or a new one named `agent` when there is none and the key may create agents (the `manage` scope; otherwise 403 `insufficient_scope`). With several agents and a key tied to none, it is required (400 `missing_field`).
  - `to` union, required: Who the draft goes to: one address, or a list of 1 to 50. It is stored and returned as a list.
    - string, email
    - string[]
  - `cc` string[]: Addresses to copy when the draft is sent, up to 50.
  - `bcc` string[]: Addresses to blind-copy when the draft is sent, up to 50. They appear in none of the email's headers.
  - `subject` string: Subject line, 1 to 998 characters. A draft without one is sent with an empty subject.
  - `body_text` string: Plain-text body, up to 1,000,000 characters. A draft can be saved without a body.
  - `body_html` string: HTML body, up to 1,000,000 characters. A draft can be saved without a body.
  - `reply_to` string, email: The address replies to the sent draft should go to. Left out, replies go to the agent's `receiving_address`.
  - `in_reply_to_message_id` string: Id of the message this one answers (`msg_…` or `rcv_…`), named in the email's `In-Reply-To` header. Once sent, the draft joins its thread when the same agent sent or received it. Left out, it starts a new thread.
  - `send_at` string, date-time: Schedule the draft to send at this time: an ISO 8601 UTC time ending in `Z`, in the future (400 `invalid_field` otherwise). It needs the `send` scope. Left out, the draft waits until it is sent.
  - `tags` object[]: Up to 10 name-value labels, copied onto the message when the draft is sent; they are not part of the email.
    - `name` string, required: Tag name: 1 to 256 letters, digits, `_` or `-`.
    - `value` string, required: Tag value, up to 256 characters; it may be empty.
  - `list_unsubscribe` boolean: Opt in to RFC 8058 one-click unsubscribe: the message carries `List-Unsubscribe` and `List-Unsubscribe-Post: List-Unsubscribe=One-Click` headers, so the recipient's mail app shows an Unsubscribe button. One click suppresses the address for your whole workspace: every later send to it from any of your agents is refused with `recipient_suppressed` until you allowlist it (`POST /v1/suppression/allow`). Turn it on for newsletters, digests and other recurring mail. Leave it off (the default) for password resets, sign-in links, receipts and anything else the recipient must keep receiving. Needs exactly one `to` recipient and no `cc` or `bcc`.

## Response `201`

The created draft.

- Draft
  - `id` string, required: Draft id (`dft_…`).
  - `agent_id` string, required: Id (`agt_…`) of the agent the draft is sent from, chosen when it was created.
  - `to` string[]: The draft's recipients, always as a list, even when one address was given.
  - `cc` string[]: The draft's Cc addresses. Absent when `cc` was never given; `[]` when it was given empty or a PATCH emptied it.
  - `bcc` string[]: The draft's Bcc addresses, who receive it but appear in none of its headers. Absent when `bcc` was never given; `[]` when it was given empty or a PATCH emptied it.
  - `subject` string: The draft's subject. Absent when none was given; the draft is then sent with an empty subject.
  - `body_text` string: The draft's plain-text body. Absent when none was given.
  - `body_html` string: The draft's HTML body. Absent when none was given.
  - `reply_to` string, email: The address replies should go to, as given on create; absent when none was.
  - `in_reply_to_message_id` string: Id of the message this draft answers (`msg_…` or `rcv_…`), as given on create; absent when none was.
  - `thread_id` string: The thread the draft's message joined (`thrd_…`), written when the draft is sent: the thread of the message named by `sent_message_id`, also when the send could not be fully recorded after that message was stored (see `error_message`). Absent until it is sent, when a send could not be recorded far enough to store its message (no `sent_message_id`), once that thread is deleted, and on drafts sent before this field was filled in.
  - `tags` Tag[]: Name-value labels saved with the draft and copied onto the message when it is sent. Absent when none were given.
    - `name` string, required: The tag's name, as given on the send, reply or draft.
    - `value` string, required: The tag's value, as given; it may be an empty string.
  - `send_at` string, date-time: The time the draft is scheduled for (UTC); it stays after the draft is sent. Absent when it was never scheduled or was unscheduled with `send_at: null`.
  - `list_unsubscribe` boolean: Whether the draft is sent with RFC 8058 one-click unsubscribe headers (`list_unsubscribe` on create or update; off by default). One click suppresses the recipient for the whole workspace.
  - `status` 'draft' | 'scheduled' | 'sending' | 'sent' | 'failed' | 'canceled', required: `draft`: saved, not scheduled. `scheduled`: waiting for `send_at`. `sending`: being sent now. `sent`: see `sent_message_id`. `failed`: the send failed, see `error_message`. `canceled`: a scheduled reply whose conversation was deleted before it went out. Only `draft` and `scheduled` drafts can be edited or sent.
  - `sent_message_id` string: Id (`msg_…`) of the message the draft became when it was sent. Absent on a draft not yet sent; a sent draft whose send could not be fully recorded (see `error_message`) can lack it too.
  - `error_message` string: Why the draft is `failed` or `canceled`. On a `sent` draft it reports a send that went out but could not be fully recorded. Absent otherwise.
  - `created_at` string, date-time, required: When the draft was created (UTC).
  - `updated_at` string, date-time, required: When the draft last changed: an edit, a new schedule, or a status change as it is sent.

## Other responses

- `400`: Invalid request — malformed JSON or a field failed validation. Also `feature_not_enabled` when this server has a feature switched off (custom domains before they are available).
- `401`: Missing or invalid API key.
- `403`: The API key lacks the required scope (`insufficient_scope`), or the token belongs to an app connected by sign-in and this operation is closed to connected apps (`connected_app_not_allowed`).
- `404`: Resource not found in this workspace, or the agent a send names is archived (`agent_archived`).
- `422`: Unprocessable — agent paused, recipient suppressed, a recipient at test.mails.ai other than reply@test.mails.ai (`unknown_test_address`), or abuse guard.
- `500`: Internal server error.

---

[API](https://skmtc.dev/mails/apis/mails-ai-api.md) · [All operations](https://skmtc.dev/mails/apis/mails-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mails/apis/mails-ai-api/revisions/4d4930a8c33b?raw)
