Skip to main content
Agentic Mail Center DocsBack to product

Agent REST API v1

AgenticMailCenter is an email inbox for external agents. An external agent runs in a client you choose; the dashboard is a companion for mailbox owners. This page documents the small, tenant-bound REST surface available to agents. The machine-readable contract is available at /openapi. It contains exactly the supported 20 operations; browser, administration, mailbox lifecycle, audit, and internal routes are not part of this API.

Authentication and scopes

Create a tenant API key in the authenticated operator workspace, store it in a secret manager, and send it as Authorization: Bearer $AGENT_API_KEY. Keys are tenant-bound and independently revocable. The required scope is recorded per operation in OpenAPI as x-agent-scope.

The safe read example below is deliberately side-effect free:

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${AGENT_API_KEY}" \
  "${AGENTICMAILCENTER_ORIGIN}/v1/accounts"

The same request in TypeScript:

const response = await fetch(`${process.env.AGENTICMAILCENTER_ORIGIN}/v1/accounts`, {
  headers: { Authorization: `Bearer ${process.env.AGENT_API_KEY}` },
});
if (!response.ok) throw new Error(`Agent API failed: ${response.status}`);
const accounts = await response.json();
console.log(accounts);

And Python using only the standard library:

import os
from urllib.request import Request, urlopen

request = Request(
    f"{os.environ['AGENTICMAILCENTER_ORIGIN']}/v1/accounts",
    headers={"Authorization": f"Bearer {os.environ['AGENT_API_KEY']}"},
)
with urlopen(request) as response:
    print(response.read().decode("utf-8"))

Read message text

GET /v1/messages/{message_id}/content reads a message's text from the connected email account when your agent needs it. It requires gateway:read, and each read appears in the audit trail. Hosted deployments do not store message bodies, so use this route rather than the body fields on GET /v1/messages/{message_id}:

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${AGENT_API_KEY}" \
  "${AGENTICMAILCENTER_ORIGIN}/v1/messages/${MESSAGE_ID}/content"

The response contains content.plain and content.html. Each read returns up to 1 MB of text, and content.truncated is true when some of it was left out.

Thread sorting and pagination

GET /v1/threads accepts sort=received_at|subject|sender_name|sender_email and direction=asc|desc, defaulting to newest received first. Received means provider mailbox arrival, not the Date header or import time. Sender fields come from the latest message in the conversation; missing sender names use the address. Unknown values sort last, with stable ID ties.

Use limit (1-100, default 50) and pass the returned next_cursor as cursor to continue. Keep the account, filters, sort, direction, and compact option unchanged while following a cursor. Start a new first page when any of them changes. include_matched_message_ids=false omits the per-thread message-ID arrays while retaining matching counts and bounded representative metadata; the default remains true for existing callers.

Draft and send boundary

Agents can create and update drafts with gateway:write, then submit them for review. The no-send journey is: POST /v1/drafts, PATCH /v1/drafts/{draft_id}, then POST /v1/drafts/{draft_id}/submit. Submitting does not send. For send_with_policy, sending requires gateway:send, human approval, and passing policy checks. The separate send_direct mode can send a policy-allowed draft before review only when AMG_DIRECT_SEND_ENABLED=true; rejected drafts remain blocked. The dashboard's eligibility check does not send or change draft state. Use OpenAPI as the source of truth for paths, scopes, and parameters; examples use the runtime draft payload and recipient shape.

curl --fail-with-body --silent --show-error -X POST \
  -H "Authorization: Bearer ${AGENT_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"account_id":"acct_example","agent_identity_id":"ident_example","idempotency_key":"draft-example-001","subject":"Example subject","body_text":"Example body","to_addresses":[{"address":"recipient@example.invalid"}]}' \
  "${AGENTICMAILCENTER_ORIGIN}/v1/drafts"

curl --fail-with-body --silent --show-error -X PATCH \
  -H "Authorization: Bearer ${AGENT_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Updated example subject","body_text":"Updated example body","to_addresses":[{"address":"recipient@example.invalid"}]}' \
  "${AGENTICMAILCENTER_ORIGIN}/v1/drafts/draft_example"

curl --fail-with-body --silent --show-error -X POST \
  -H "Authorization: Bearer ${AGENT_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"submitted_by":"agent_example"}' \
  "${AGENTICMAILCENTER_ORIGIN}/v1/drafts/draft_example/submit"

Never put a key in a URL, commit it, paste it into a ticket, or use a production key while testing examples. The placeholders above are inert and use no real mailbox data. Verify reads locally or against a non-production tenant and do not send mail during documentation checks.