Inboxes
An inbox is a real address that an agent can receive, inspect, organise and answer over the API. Each inbox belongs to a tenant and has its own threads, folders, messages, attachments, drafts and outbound policy.
Lifecycle
| Method | Path | Access | Purpose |
|---|---|---|---|
POST |
/v1/tenants/{tenant_id}/inboxes |
admin / tenant admin | Create an inbox |
GET |
/v1/inboxes |
read |
List inboxes; filter with tenant_id |
GET |
/v1/inboxes/{id} |
read |
Read configuration |
PATCH |
/v1/inboxes/{id} |
admin / tenant admin | Change display_name, status, policy, metadata |
DELETE |
/v1/inboxes/{id} |
admin / tenant admin | Soft-delete |
POST |
/v1/domains/{id}/inbound |
admin / tenant admin | Enable or disable receiving on a domain ({"enabled": true}) |
Create:
{
"domain_id": "DOMAIN_ID",
"local_part": "support",
"display_name": "Support Agent",
"policy": {
"dlp": "enforce",
"max_send_per_hour": 100,
"allow_recipients": ["customer.example"],
"require_approval_on_taint": true
},
"metadata": {"purpose": "customer-support"}
}
status is active or paused; a paused inbox refuses compose operations with 409 inbox_paused. provider is native or, for connected mailboxes, google / microsoft.
Lists are newest first; pass the last item’s created_at as cursor for the next page. limit defaults to 50, max 200.
Reading mail
GET /v1/inboxes/{id}/messages
| Parameter | Meaning |
|---|---|
folder |
inbox (default view, excludes trash), sent, drafts, archive, spam, trash, a custom folder, or all |
unread |
true → unread inbound only |
direction |
in or out |
label |
Exact label |
from |
Case-insensitive substring on the sender |
cursor |
An older message’s created_at |
limit |
Default 50, max 200 |
A message:
{
"id": "MESSAGE_ID",
"inbox_id": "INBOX_ID",
"thread_id": "THREAD_ID",
"direction": "in",
"from": "customer@example.net",
"to": ["support@agents.example.com"],
"subject": "I need help",
"snippet": "I need help with...",
"text": "I need help with my order.",
"extracted_text": "I need help with my order.",
"verdict": "clean",
"auth": {"spf": "pass", "dkim": "pass", "dmarc": "pass"},
"shield": {},
"labels": [],
"folder": "inbox",
"read": false,
"has_html": false,
"attachments": [{"id": "ATT_ID", "filename": "receipt.pdf", "content_type": "application/pdf", "size": 48213}],
"created_at": "2026-10-03T18:01:00Z"
}
extracted_text includes text pulled from attachments where possible, so a model can read a PDF invoice without a separate parsing step.
Verdicts
Every inbound message gets a Shield verdict: clean, suspicious, spam, blocked or unauthenticated. spam and blocked land in the spam folder automatically. The verdict is a safety signal, not permission: anything other than clean should be treated as untrusted data, and no verdict means the message’s instructions should be executed.
Threads, search, raw content
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/inboxes/{id}/threads |
Threads, newest activity first (cursor = last_message_at) |
GET |
/v1/threads/{id} |
One thread |
GET |
/v1/threads/{id}/messages |
A thread’s messages |
GET |
/v1/inbox-messages/{id} |
One message with attachment metadata |
GET |
/v1/inboxes/{id}/search?q=… |
Full-text search over subject, body and extracted attachment text; optional folder, limit |
GET |
/v1/inbox-messages/{id}/raw |
RFC 5322 source as message/rfc822 |
GET |
/v1/inbox-messages/{id}/html |
{ "id", "html" } — returned as data, never rendered |
GET |
/v1/inbox-attachments/{id} |
Attachment bytes (application/octet-stream, original type in X-Attachment-Content-Type) |
Send, reply and forward
The inbox address is always the From; clients cannot override it. Outbound mail uses the same quotas, suppressions, DKIM and retries as relay sends, plus the inbox’s Shield policy.
| Method | Path | Purpose |
|---|---|---|
POST |
/v1/inboxes/{id}/messages |
Compose a new message |
POST |
/v1/inbox-messages/{id}/reply |
Reply in the existing thread (reply_all: true to include original To/Cc) |
POST |
/v1/inbox-messages/{id}/forward |
Forward; requires at least one to |
Body (all three):
{
"to": ["human@example.net"],
"cc": [], "bcc": [],
"subject": "Follow-up",
"text": "Thanks for the details.",
"html": "<p>Thanks for the details.</p>",
"reply_all": false,
"attachments": [{"filename": "details.pdf", "content_type": "application/pdf", "content": "<base64>", "content_id": "details", "inline": false}],
"send_at": "2026-10-05T09:00:00Z"
}
At least one of text/html and one recipient. Attachments total ≤ 25 MiB. Replies default to the original sender and a Re: subject; forwards derive Fwd:.
Responses (all 202):
{ "message_id": "OUT_ID", "queued": 1, "thread_id": "THREAD_ID", "inbox_message_id": "SENT_COPY_ID" }
{ "held": true, "approval_id": "APPROVAL_ID", "reason": "dlp" }
{ "scheduled": true, "draft_id": "DRAFT_ID", "scheduled_at": "2026-10-05T09:00:00Z" }
Folders, moving, labels
System folders: inbox, sent, drafts, archive, spam, trash. Up to 100 custom folders (1–64 chars, case-insensitively unique, no /, \ or control characters).
| Method | Path | Purpose |
|---|---|---|
GET |
/v1/inboxes/{id}/folders |
Folders with total and unread |
POST |
/v1/inboxes/{id}/folders |
{ "name": "Invoices" } |
PATCH / DELETE |
/v1/inboxes/{id}/folders/{name} |
Rename / delete (non-empty requires ?move_to=) |
POST |
/v1/inboxes/{id}/folders/{name}/empty |
Permanently empty trash or spam |
POST |
/v1/inbox-messages/{id}/move |
{ "folder": "archive" } |
DELETE |
/v1/inbox-messages/{id} |
To trash; from trash (or ?permanent=true) delete for good |
POST |
/v1/inboxes/{id}/batch |
move, delete, read or unread on 1–200 IDs |
POST |
/v1/inbox-messages/{id}/read |
{ "read": true } |
PUT |
/v1/inbox-messages/{id}/labels, /v1/threads/{id}/labels |
Replace labels |
Nothing can be moved into sent or drafts. Unread mail in spam/trash does not count toward thread unread totals.
Drafts and scheduled send
Drafts live in drafts. Sending one removes it and creates the sent copy (or an approval if policy requires review). Scheduled drafts are sent by the server at send_at, through the same policy.
| Method | Path | Purpose |
|---|---|---|
GET / POST |
/v1/inboxes/{id}/drafts |
List / create (compose fields + optional reply_to_id, send_at) |
GET / PATCH / DELETE |
/v1/drafts/{id} |
Read / replace / delete |
POST |
/v1/drafts/{id}/send |
Send now |
POST |
/v1/drafts/{id}/schedule |
Set or replace send_at |
POST |
/v1/drafts/{id}/cancel |
Cancel the schedule, keep the draft |
Events: message.draft_saved, message.scheduled, message.draft_sent.
AI auto-label and extraction
When AI is enabled on the account (PATCH /v1/account, ai_enabled), inbound mail can be auto-labelled and structured data extracted on demand:
curl -X POST "$API/v1/inbox-messages/$MESSAGE_ID/extract" \
-H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
-d '{"schema": {"type":"object","required":["order_id"],"properties":{"order_id":{"type":"string"}},"additionalProperties":false}, "save": true}'
Message content is treated as untrusted data; model output is returned to you and cannot trigger actions. Available from the Developer plan with a monthly token cap (100k on Developer, 1M on Startup). Auto-enrichment emits message.enriched. The same capability is exposed as the extract_data MCP tool.
Connected Gmail and Microsoft 365
An inbox can be backed by a real Gmail or Microsoft 365 mailbox instead of the native store, so an agent works an existing human mailbox with the same API, folders, search, streams, Shield and IMAP access.
| Method | Path | Purpose |
|---|---|---|
POST |
/v1/inboxes/{id}/connect/google |
Returns auth_url; redirect the admin there |
POST |
/v1/inboxes/{id}/connect/microsoft |
Same for Microsoft |
DELETE |
/v1/inboxes/{id}/connect |
Disconnect, revoke tokens, revert to native |
OAuth tokens are sealed with the account’s data key. Connected mailboxes are available on Startup and Enterprise plans.