---
title: "Drafts"
description: "Compose now, send later — the approval gate between an agent and an outbox."
canonical_url: "https://www.agent-identity.dev/docs/mail/drafts"
markdown_url: "https://www.agent-identity.dev/docs/mail/drafts.md"
last_updated: "2018-10-20"
x_farming_labs_generated_preamble: true
agent:
  tokenBudget: 700
  task: "Have an agent compose a message a human approves before it leaves."
  outcome: "A draft exists and is not delivered until it is explicitly sent, at which point it behaves like any other outbound message."
  appliesTo:
    package:
      - "@aid/api"
      - "@agentidentity/sdk"
  prerequisites:
    - "An agent with a provisioned mailbox."
  files:
    - "apps/api/src/services/mail.ts"
  sideEffects:
    - "Sending a draft consumes outbound quota and appends a mail.sent event, exactly as a direct send does."
  verification:
    - description: "Confirm the draft is unsent."
      expect: "It appears in the drafts list and produces no mail.sent event until sent."
  rollback:
    - "Delete the draft. Once sent, a message cannot be recalled."
  failureModes:
    - symptom: "A draft cannot be edited."
      resolution: "It has already been sent. Sent messages are immutable; reply on the thread instead."
---

# Drafts
URL: /docs/mail/drafts
LLM index: /llms.txt
Description: Compose now, send later — the approval gate between an agent and an outbox.
Related: /docs/mail/sending, /docs/mail/attachments

<!-- farming-labs:agent-contract:start -->
## Agent Contract

Task: Have an agent compose a message a human approves before it leaves.
Outcome: A draft exists and is not delivered until it is explicitly sent, at which point it behaves like any other outbound message.

### Applies To

- Package: `@aid/api`, `@agentidentity/sdk`

### Prerequisites

- An agent with a provisioned mailbox.

### Files

- `apps/api/src/services/mail.ts`

### Side Effects

- Sending a draft consumes outbound quota and appends a mail.sent event, exactly as a direct send does.

### Verification

- Confirm the draft is unsent.
  - Expected: It appears in the drafts list and produces no mail.sent event until sent.

### Rollback

- Delete the draft. Once sent, a message cannot be recalled.

### Failure Modes

- A draft cannot be edited. — Recovery: It has already been sent. Sent messages are immutable; reply on the thread instead.
<!-- farming-labs:agent-contract:end -->

# Drafts

A draft is a message that exists but has not been handed to the provider. It is the
natural place to put a human in the loop: the agent writes, someone reads, and only then
does it go out.

```bash title="terminal"
curl -sS -X POST "https://api.agent-identity.dev/v1/identities/$AID_AGENT/mail/drafts" \
  -H "authorization: Bearer $AID_KEY" \
  -H 'content-type: application/json' \
  -d '{"to":["customer@example.com"],"subject":"Refund","text":"Processing your refund now."}'
```

Every field is optional on a draft, including `to` — an agent can start one before it
knows who it is writing to. The same fields as [send](/docs/mail/sending) are accepted,
plus `in_reply_to_message_id` and `attachments`.

| Operation | Endpoint |
| --- | --- |
| Create | `POST /v1/identities/:id/mail/drafts` |
| List | `GET /v1/identities/:id/mail/drafts` |
| Update | `PATCH /v1/identities/:id/mail/drafts/:messageId` |
| Delete | `DELETE /v1/identities/:id/mail/drafts/:messageId` |
| Send | `POST /v1/identities/:id/mail/drafts/:messageId/send` |

Sending a draft turns it into a normal outbound message — same `202`, same queue, same
`mail.sent` event. The draft is not left behind as a separate copy.

<Callout type="info">
  Validation that a send would reject — an empty recipient list, a blocked address —
  applies when the draft is sent, not when it is saved. A draft that saves cleanly can
  still fail to send.
</Callout>

- `draftMailBody` makes **every** field optional, unlike `sendMailBody` which requires
  `to` with `min(1)`. Enforce recipients yourself before calling the send endpoint if you
  want the failure earlier.
- `PATCH` takes the same body shape minus `identityId` and `inReplyToMessageId` — the
  thread a draft belongs to is fixed at creation and cannot be changed by an update.
- `POST …/drafts/:messageId/send` does not accept a body. To change content first, `PATCH`
  then send.
- Drafts do not appear in `GET …/mail/messages`; they are only in `GET …/mail/drafts`.
  After sending, the reverse is true.

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
