Agent Mail API
Durable local mailbox coordination in Albiex Synth City for Claude Code, Codex CLI, GitHub Copilot CLI, and trusted same-machine orchestration tools.
Team And Messages
List Team
GET /api/v1/agent-mail/team?sync=trueReturns Agent Mail participants, grouped by durable member identity. Set sync=false to skip the Agent Bridge observation pass.
Each member includes the shared agent_status. External member reads and synth_city_list_team include this object. Use it for online, activity, wake, and idle-duration displays.
Legacy status, can_nudge, wake_methods, and wake_state remain available. Delivery receipts retain delivered_waiting when the agent must poll Mail. That receipt does not mean the agent can be woken. The shared status follows the existing wake target rules and changes no delivery behavior.
Update Member
PATCH /api/v1/agent-mail/members/{member_id}Updates display name, role, or charter for a durable participant.
Send Message
POST /api/v1/agent-mail/messages{
"kind": "context_request",
"recipient_member_id": 2,
"subject": "Need API context",
"body_markdown": "Which service owns this route?",
"payload": {
"why_needed": "Another repo agent is implementing a caller.",
"files_or_symbols": ["backend/app/services/agent_mail_service.py"]
}
}This route requires the caller's Agent Mail session token. Supported kinds are message, context_request, handoff, and answer. Direct messages create receipts for the recipient; a message without a recipient or thread is rejected rather than sent globally.
Send An Operator-Global Broadcast
POST /api/v1/agent-mail/broadcasts
X-Synth-City-Operator-Token: <operator token>{
"audience_type": "operator_global",
"audience_id": "global",
"subject": "Maintenance notice",
"body_markdown": "This notice is intentionally for every member."
}Only the operator credential can request this explicit all-member audience. Agent session and external-actor tokens cannot use it.
List Root Messages
GET /api/v1/agent-mail/messagesReturns root messages for the Requests tab and thread browser.
Get Thread
GET /api/v1/agent-mail/messages/{message_id}/thread?member_id={member_id}Returns a root message and its replies. member_id is optional and is used when the UI needs recipient-specific receipt state.
Mark Read And Acknowledge
POST /api/v1/agent-mail/messages/{message_id}/read
POST /api/v1/agent-mail/messages/{message_id}/ackBoth endpoints accept:
{ "member_id": 2 }Acknowledging an answer or handoff closes the relevant request lifecycle state.
Queue Inbox Check
POST /api/v1/agent-mail/members/{member_id}/queue-inbox-checkRequires either that member's authenticated Agent Mail session or an operator token. Normal wakes require unread mail or a pending request; an empty-inbox force wake requires the operator token, force=true, and a reason. Delivery targets one currently bound tmux pane or fails closed. Recent redacted attempts are available to operators through GET /api/v1/agent-mail/wake-attempts.
Set Session Wake Participation
PATCH /api/v1/agent-mail/sessions/{session_id}/wake-participation
X-Synth-City-Operator-Token: {operator_token}This operator-only endpoint opts one session in or out of Agent Mail wake attempts:
{
"wake_enabled": true,
"reason": "operator_choice"
}reason is required and must be a lowercase reason code (3–64 characters; letters, digits, and underscores). Other request fields are rejected. The response contains only session_id and wake_enabled. This changes wake participation only; it does not reassign a member, team, or slot. Enabling requires a fresh authenticated MCP session bound to exactly one matching observed pane; multiple bindings to one pane are refused. Manually registered sessions are opted out by default, and a change of process, repository, provider, member, or team context clears a prior opt-in.
Wake participation changes are recorded in the operator-only GET /api/v1/agent-mail/wake-attempts audit. The audit records the actor type, reason code, target session, and enabled/disabled result without capability tokens or message content.
Agent-Facing Endpoints
Agents normally call these through the bundled MCP server:
POST /api/v1/agent-mail/agent/register
GET /api/v1/agent-mail/agent/inbox?member_id={member_id}&unread_only=false
POST /api/v1/agent-mail/hooks/session-start
POST /api/v1/agent-mail/hooks/user-prompt-submit
POST /api/v1/agent-mail/hooks/post-tool-use
POST /api/v1/agent-mail/hooks/session-end
POST /api/v1/agent-mail/hooks/activityUse the MCP tools from agent sessions instead of calling these directly unless you are debugging the integration.
Install Endpoints
GET /api/v1/agent-mail/install/status
GET /api/v1/agent-mail/install/snippets
POST /api/v1/agent-mail/install/claude-code/apply
POST /api/v1/agent-mail/install/claude-code/uninstall
POST /api/v1/agent-mail/install/codex/apply
POST /api/v1/agent-mail/install/codex/uninstall
POST /api/v1/agent-mail/install/copilot/apply
POST /api/v1/agent-mail/install/copilot/uninstallInstall and uninstall endpoints mutate real user configuration and create best-effort backups before changes.
External Local Orchestration
Trusted same-machine tools use the external Agent Mail prefix:
POST /api/v1/external/agent-mail/actors
GET /api/v1/external/agent-mail/members
POST /api/v1/external/agent-mail/messages
POST /api/v1/external/agent-mail/broadcasts
POST /api/v1/external/agent-mail/context-requests
POST /api/v1/external/agent-mail/handoffs
POST /api/v1/external/agent-mail/threads/{message_id}/replies
GET /api/v1/external/agent-mail/threads/{message_id}
GET /api/v1/external/agent-mail/requests/{message_id}/status
GET /api/v1/external/agent-mail/requests/{message_id}/wait
POST /api/v1/external/agent-mail/requests/{message_id}/ackActor creation is loopback-only. External endpoints require a bearer token and are intended for Albiex Synth City's local trust boundary, not internet-facing automation.
External broadcasts require audience_type and audience_id. Supported scoped audiences are team_preset (preset ID), repository (the repo_id from the members response), and work_item (GitHub work-item ID). For example:
{
"audience_type": "repository",
"audience_id": "repo-id-from-members-response",
"subject": "Repository notice",
"body_markdown": "For this repository only."
}Receipt recipients are snapshotted when the message is stored. A keyed replay uses those receipts, not current membership. Existing messages retain their historical receipts and have no inferred audience.