# Agent quickstart

> Connect an AI agent to AY Inbox: create a scoped key or sign in with OAuth, list the tools it may use, triage replies, draft answers and approve one safely.

Source: https://docs.ayinbox.com/api/agent-quickstart/

To give an agent AY Inbox, create an API key from the **Agent: read and draft** preset (or sign in over OAuth), point the agent at the MCP server or the REST tool endpoints, and start it on the `inbox` toolset. It can read every thread and draft replies; sending stays with a key that holds a send scope.

This page walks one loop end to end: credential, connection, tool list, triage, draft, approval. Each step links to the full reference. For why the boundaries sit where they do, see [Cold email for AI agents](/learn/cold-email-for-ai-agents/).

1. Pick a credential [#1-pick-a-credential]

| Credential         | Best for                                                             | How you get it                                                                                                                                                        |
| ------------------ | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OAuth access token | An MCP client a person connects: Claude Code, Claude Desktop, Cursor | The client runs the sign-in itself; you approve scopes in the browser                                                                                                 |
| API key            | A server, a script, CI, a function-calling agent you host            | Settings > API keys, then copy the `aybox_` secret, shown once                                                                                                        |
| Agent sign-up key  | An agent with no account and no person at hand                       | `POST /v1/agents/register` opens a new workspace and answers with a key that sets up contacts and draft campaigns but never sends. See [For agents](/api/for-agents/) |

An OAuth client that registered itself gets the scopes it asked for that your role covers, and never `SEND_CAMPAIGNS`, `API_KEYS` or `WRITE_WORKSPACE`. Its access token lasts one hour and refreshes with a rotating refresh token. See [OAuth 2.1](/api/oauth/).

For a key, pick the **Agent: read and draft** preset (`84574239`). It is every read scope plus `AI_DRAFT`:

| Scope                               | Lets the agent                                                 |
| ----------------------------------- | -------------------------------------------------------------- |
| `READ_UNIBOX`                       | Read the unified inbox and the inbox agent's held drafts       |
| `READ_CONTACTS`, `READ_CRM`         | Read contacts, segments, deals and tasks                       |
| `READ_CAMPAIGNS`, `READ_TEMPLATES`  | Read campaigns, steps and reply templates                      |
| `READ_EMAILS`, `READ_ANALYTICS`     | Read mailboxes, health and stats                               |
| `READ_AUDIT_LOGS`, `READ_WORKSPACE` | Read the audit trail and the workspace profile and limits      |
| `AI_DRAFT`                          | Have the AI write reply drafts, charged to credits, never sent |

A key never holds more than the role of the member who created it. You can also limit it to some mailboxes or IP addresses and give it an expiry; see [Authentication](/api/authentication/). Check what a credential is with `GET /v1/me`, which returns its workspace, `auth_type` and `scopes`.

2. Connect [#2-connect]

**MCP.** The server is `https://api.ayinbox.com/v1/mcp` over streamable HTTP. With OAuth, Claude Code needs one command:

```bash
claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcp
```

With a key, add the header `Authorization: Bearer aybox_...`. Client-by-client config is in [Connect with OAuth](/api/mcp/#connect-with-oauth) and [Connect with an API key](/api/mcp/#connect-with-an-api-key).

**REST tools.** A function-calling agent lists tools with `GET /v1/ai/tools` and runs one with `POST /v1/ai/tools/{name}/call`, sending the tool's arguments as the JSON body. See [Agent tools (REST)](/api/agent-tools/).

3. List what it can do [#3-list-what-it-can-do]

Start a small model on the `inbox` toolset so it is not choosing among every tool in the catalog:

```bash
curl -s "https://api.ayinbox.com/v1/ai/tools?format=openai&toolsets=inbox" \
  -H "Authorization: Bearer aybox_..."
```

On MCP the same filter is a query string: `https://api.ayinbox.com/v1/mcp?toolsets=inbox`. It is tested with API keys; an OAuth client that ties its token to the exact server URL may not accept it, so use the plain URL there and it lists every tool the credential may call. The list holds only the tools this credential may call, so you can hand it to the model as it is. `format` takes `ayinbox` (the default), `openai`, or `hermes`. Widen later with a comma-separated list, for example `inbox,contacts,crm`.

4. Triage [#4-triage]

`list_threads` finds conversations and `get_thread` reads one:

```bash
curl -s -X POST "https://api.ayinbox.com/v1/ai/tools/list_threads/call" \
  -H "Authorization: Bearer aybox_..." \
  -H "Content-Type: application/json" \
  -d '{"unseen_only": true, "limit": 20}'
```

`list_threads` also takes `subject`, `sender`, `folder` and `awaiting_reply` (threads where your mailbox spoke last). `get_thread` takes a `thread_id` and an optional `limit` of messages. `get_inbox_overview` and `get_unread_count` give the inbox at a glance.

Results are data, not instructions. On REST, `result` is the raw JSON for your code, and `result_text` is the same output fenced between `<<<UNTRUSTED_CONTENT>>>` markers: give the model `result_text`. On MCP every result is fenced the same way. Mail is written by people outside the workspace, and the fence tells the model none of it is a command.

5. Draft [#5-draft]

`generate_reply_draft` has the AI write a reply to the latest message in a thread, grounded on the thread, the contact and the [campaign brain](/guides/campaign-brain/) that applies (else your voice). It never sends.

```bash
curl -s -X POST "https://api.ayinbox.com/v1/ai/tools/generate_reply_draft/call" \
  -H "Authorization: Bearer aybox_..." \
  -H "Content-Type: application/json" \
  -d '{"thread_id": "THREAD_ID", "instruction": "keep it short, propose a call next week", "idempotency_key": "draft-THREAD_ID-1"}'
```

The result carries `text`, `credits_charged`, `credits_remaining` and, when a brain applied, `grounding` with any `flags`. It needs `READ_UNIBOX` and `AI_DRAFT`, and charges what a dashboard reply draft charges: 2 [AI credits](/guides/ai-credits/) as a base, more for a longer prompt or a brain with material, within the workspace's spend limits.

`idempotency_key` is required and pays for one draft. A retry with the same key after a success is refused, uncharged, with no second draft, so use a new key for a new draft. A retry after the model failed (which gives the credits back) is charged again. When the agent writes the text itself, `draft_reply` checks it against the brain and returns `flags` without calling a model.

6. Approve [#6-approve]

Approving sends mail, so it is not a tool. It belongs in your own code, on a separate key that holds `WRITE_UNIBOX`, after a person or your own rules have read the draft. There are two kinds of draft:

1. **Inbox agent drafts.** The [inbox agent](/guides/inbox-agent/) drafts suggested replies and holds them. The agent lists them with `list_agent_drafts`; your code approves one with `POST /v1/unibox/agent-drafts/:id/approve`, optionally with an edited `body`. `discard_agent_draft` or `POST /v1/unibox/agent-drafts/:id/discard` drops one; both need `WRITE_UNIBOX`, which the agent preset does not hold.
2. **Drafts from `generate_reply_draft`.** The text comes back to you and is not stored. Your code sends the reviewed text with `POST /v1/unibox/reply`.

A key or OAuth caller approving an agent draft whose `grounding.flags` is not empty gets `409 draft_flagged`, with the flags, and the draft stays pending. Read them, then approve again:

```bash
curl -s -X POST "https://api.ayinbox.com/v1/unibox/agent-drafts/DRAFT_ID/approve" \
  -H "Authorization: Bearer aybox_SEND_KEY..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: approve-DRAFT_ID-2" \
  -d '{"acknowledge_flags": true}'
```

Send the acknowledged approval with a new `Idempotency-Key`, or none: the same key with a different body answers `409`. A flag names a link, address, phone number, figure or phrase the brain does not back, so treat it as a reason to edit. The full pattern is in [Approve AI drafts](/api/recipes/approve-ai-drafts/), and the endpoint is in the [unibox reference](/api/reference/unibox/).

Next [#next]

* **Booking.** `get_availability` and `offer_meeting_times` prepare open times for a reply; booking itself is `POST /booking/offers/:id/book`. See [booking](/guides/booking/).
* **Auto-reply.** A campaign can answer qualifying replies itself within approved links and figures; turning it on needs `SEND_CAMPAIGNS`. See [auto-reply](/guides/auto-reply/).
* **Long-running agents.** Rather than polling `list_threads`, subscribe to the [realtime API](/api/realtime/) with a key holding `REALTIME_SUBSCRIBE`, or receive [webhooks](/guides/webhooks/).
* **Scopes.** Every scope, preset and the member permission behind each is in [Permissions](/api/permissions/).

## Frequently asked questions

### What permissions does the Agent: read and draft preset include?

Every read scope (READ_EMAILS, READ_CAMPAIGNS, READ_CONTACTS, READ_UNIBOX, READ_ANALYTICS, READ_TEMPLATES, READ_CRM, READ_AUDIT_LOGS and READ_WORKSPACE) plus AI_DRAFT, for a bitmask of 84574239. It holds no scope that writes or sends.

### Can an AI agent approve and send its own reply drafts?

Not through the agent tools: no MCP or REST tool sends mail, and approving an inbox agent draft is a REST call that needs WRITE_UNIBOX. Keep that call in your own code, on a separate key, after a person or your own rules have checked the draft.

### How many AI credits does generate_reply_draft use?

generate_reply_draft charges what a dashboard reply draft charges: 2 AI credits as a base, more for a longer prompt or a campaign brain with material. The result reports credits_charged and credits_remaining.

### What does 409 draft_flagged mean?

The draft contains a link, address, phone number, figure or phrase the campaign brain does not back. The draft stays pending; read the flags in the response, then approve again with acknowledge_flags set to true and a new Idempotency-Key, or edit the body first.

### Why does my agent's retry of generate_reply_draft fail?

Each idempotency_key pays for one draft. A retry with a key whose draft already succeeded is refused with nothing charged and no second draft, so pass a new key when you want a new draft.
