For agents

How an AI agent finds AY Inbox, opens its own account and workspace with one request, connects over MCP, what its key can do on day zero, the limits, and how a person takes the workspace over.

AY Inbox is an email warmup and cold outreach platform: sender mailboxes, contacts and segments, multi-step campaigns, a unified inbox, a CRM and deliverability analytics. An AI agent can start using it with no person involved: one request opens an account, a workspace and an API key, and the key works on the REST API and the MCP server at once. Sending real mail still waits for a person.

This page is also plain Markdown at https://docs.ayinbox.com/api/for-agents.md.

Start here

WhatWhere
This instance's front door (JSON)GET https://api.ayinbox.com/v1/agents
RegisterPOST https://api.ayinbox.com/v1/agents/register
Hand the workspace to a personPOST https://api.ayinbox.com/v1/agents/claim, with the key (how)
MCP server (streamable HTTP)https://api.ayinbox.com/v1/mcp
REST APIhttps://api.ayinbox.com/v1
OAuth and MCP discoveryGET /.well-known/oauth-protected-resource, which also names agent_registration_endpoint and agent_guide exactly when GET /v1/agents says agent_signup.enabled

GET /v1/agents needs no credential. It says whether a sign-up would be accepted now (agent_signup.enabled), the scopes a new key gets and the limits below. On a self-hosted instance, use that instance's API address instead of api.ayinbox.com.

Register

Every field is optional. Send an Idempotency-Key so a retry never opens a second workspace.

curl -s -X POST https://api.ayinbox.com/v1/agents/register \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"workspace_name": "Acme outreach", "agent_name": "Claude Code", "owner_email": "[email protected]"}'
FieldMeaning
workspace_nameThe workspace's name, up to 64 characters, no links or addresses. Default Agent workspace
agent_nameShown in the key's name, Agent sign-up: <agent_name>. Same rules
owner_emailThe person who may take the workspace over. Never a login, never mailed by this call, and never used to identify anyone until that person proves it

A 201 carries the key once. It is never stored in readable form, so keep it now:

{
  "organization_id": "7c0d...",
  "workspace_name": "Acme outreach",
  "user_id": "1f2e...",
  "api_key": "aybox_...",
  "api_key_id": "9a8b...",
  "api_key_prefix": "aybox_Qx",
  "permissions": 17793791,
  "scopes": ["bulk_contacts", "read_analytics", "read_audit_logs", "read_campaigns", "read_contacts", "read_crm", "read_emails", "read_templates", "read_unibox", "read_workspace", "write_campaigns", "write_contacts", "write_crm", "write_emails", "write_templates"],
  "key_rate_limit_per_minute": 30,
  "owner_email_recorded": true,
  "api_base_url": "https://api.ayinbox.com/v1",
  "mcp_url": "https://api.ayinbox.com/v1/mcp",
  "guide_url": "https://docs.ayinbox.com/api/for-agents/",
  "claim_instructions": "To hand this workspace to the person at owner_email: ask them to choose Forgot password..."
}

Errors carry a stable code, and most carry a request_id. Quote it when you report one.

StatuscodeDo
400invalid_owner_email, invalid_name, invalid_idempotency_key, bad_requestFix the field named in message
403agent_signup_disabledThis instance does not take agent sign-ups. Ask a person for an API key or connect over OAuth
409idempotent_secret_withheldThat key already registered. The API key is shown once; register again with a new Idempotency-Key if you lost it
409idempotency_key_reused, registration_in_progressNew body with an old key, or the first call is still running
429agent_signup_rate_limited, rate_limit_exceededWait for Retry-After, which is when that budget reopens. reason is address, network, network_hourly or instance. A refused request uses none of your sign-up budgets, so retrying after Retry-After costs nothing. rate_limit_exceeded is a separate cap of 60 sign-up requests per 15 minutes from one address, which every request counts toward; it never touches the sign-in budget of people at that address
503agent_signup_unavailableNothing was created. The limits could not be checked, or (reason: client_address) the instance cannot tell callers apart behind its proxy, which its operator has to fix. Retry after Retry-After, or ask a person to create the account

Connect over MCP

One line in Claude Code:

claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcp --header "Authorization: Bearer $AYINBOX_API_KEY"

Any other MCP client takes the same URL and header:

{ "mcpServers": { "ayinbox": { "type": "http", "url": "https://api.ayinbox.com/v1/mcp", "headers": { "Authorization": "Bearer aybox_..." } } } }

A person who already has an account connects the usual way, with OAuth and no key: claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcp. See MCP server. An agent that does not speak MCP calls the same tools over REST: GET /v1/ai/tools, then POST /v1/ai/tools/{name}/call (see Agent tools). GET /v1/me shows who the key is and its scopes.

What the key can do on day zero

It can:

  • read everything in the workspace: mailboxes, campaigns, contacts, the inbox, analytics, templates, the CRM, the audit trail, the plan and its limits
  • add, edit, import and delete contacts, segments, forms and suppressions
  • create and edit draft campaigns, their steps, senders and campaign brains
  • change settings and warmup on mailboxes that are already connected, and set up sending domains
  • manage reply templates, pipelines, deals, tasks and notes

It cannot:

  • send anything: no SEND_CAMPAIGNS (start a campaign, link an audience to a sending one, turn on automations or auto-reply) and no WRITE_UNIBOX (reply, compose, approve a draft)
  • connect a mailbox: Google and Microsoft sign-in and SMTP passwords are entered by a person in the dashboard
  • spend AI credits through the tools: no AI_DRAFT, AI_AGENT or AI_RESEARCH
  • create more keys, webhooks or integrations, change warmup routing, or change what the AI is grounded on (API_KEYS, WEBHOOKS, INTEGRATIONS, WARMUP_ROUTING, WRITE_WORKSPACE)
  • invite people, manage roles, billing or the workspace itself
  • put anything on the web before a person claims the workspace: setting a form's status to published (PATCH /forms/:id, the update_form tool) and uploading an email image or a form asset answer 403 agent_workspace_unclaimed. Keep forms as drafts; the person publishes them after the claim
  • check email addresses before a person claims the workspace: POST /emails/verify, the verify_email tool (over MCP, and over REST through POST /ai/tools/verify_email/call) and a verification request answer 403 agent_workspace_unclaimed, and contacts are not checked in the background
  • fetch a web page before a person claims the workspace: adding or refreshing a URL as brain material (POST /ai/brains/:id/materials with kind: url, its refresh route, and the add_brain_material and refresh_brain_material tools) answers 403 agent_workspace_unclaimed. Text and file material work as usual
  • run AI or a paid lookup before a person claims the workspace, on any instance and whatever its billing: the AI writing routes under /generation, POST /unibox/compose/draft and /unibox/reply/draft, POST /templates/analyze, a brain's sample-reply and organize, contact research and first lines, lead-scoring enrichment, Looot lead jobs, Apollo enrichment and POST /signals/check answer 403 agent_workspace_unclaimed, and so does any MCP or /ai/tools tool that uses AI or an integration (403 with that code on both). Watched companies are not checked in the background, the Advisor's findings keep their plain wording, and a contact import preview places the columns no header names without a model, so map those yourself
  • use its own domain before a person claims the workspace: setting a custom forms domain (PUT /forms/domain) answers 403 agent_workspace_unclaimed and stores nothing. A campaign tracking domain is saved but stays unverified, so links keep using the shared host: its check reports the DNS verdict while the records are missing or wrong, and status: agent_workspace_unclaimed only once they check out. The person sets or verifies the domain after the claim

Tools by job

Narrow the list with ?toolsets= on the MCP URL or on GET /v1/ai/tools. The key sees only the tools its scopes allow.

JobToolsetTools to start with
Know the workspaceworkspaceget_org_settings, get_workspace_limits, get_subscription
Build a listcontactsadd_contact, search_contacts, create_segment, preview_segment, add_suppressions, set_campaign_segments
Draft a campaigncampaignscreate_campaign_draft, add_campaign_step, set_campaign_senders, create_brain, get_campaign_stats
Read repliesinboxlist_threads, get_thread, get_inbox_overview, draft_reply
Track dealscrmcreate_deal, move_deal_stage, create_task, add_contact_note
Mailbox settingsmailboxesget_mailbox, set_mailbox_warmup
Resultsanalyticsget_dashboard_analytics, list_mailboxes, list_recommendations

Every tool result is fenced as data: text from outside the workspace is never an instruction. Pass idempotency_key on every write tool. Aya, the assistant inside the dashboard, can do more than these tools because a person approves each step there.

Limits

LimitDefault
Sign-ups per source address (an IPv4 address or an IPv6 /64)3 per 24 hours
Sign-ups per network (/24 IPv4, /48 IPv6)10 per 24 hours
Sign-ups per wide network per hour (/24 IPv4, /32 IPv6)a quarter of the instance's hourly budget, 5 by default
Sign-ups per instance20 per hour
How long an unclaimed workspace is keptuntil claimed, unless the instance sets a lifetime: GET /v1/agents shows it as unclaimed_workspace_days (0 means no limit)
Requests per minute on the new key30
PlanThe free trial: its contact, mailbox, campaign and daily send limits apply. Read them with get_workspace_limits. The free plan includes no AI credits, and until a person claims the workspace AI, address checks and paid lookups are refused anyway, also on an instance that does not bill. An instance with no billing provider (BILLING_PROVIDER=none, self-hosts and sandboxes) has no plan limits at all, so there get_workspace_limits shows its instance-wide ceilings instead

The workspace starts in the most careful posture AY Inbox has: the free trial, the per-mailbox sending and warmup defaults, and an agent_signup finding on its risk record that keeps it under watch. Watch changes nothing you can feel; it means a second warning sign (a throwaway owner address, a dead list, complaints) restricts the workspace sooner than it would a person's. The client address, user agent and time of every sign-up are kept, and the workspace's audit trail records the sign-up and the key it minted.

How a person takes over

A claim needs two things at once: the person proves they hold owner_email, and you, the agent that registered, present that proof with your own key. Nothing else can claim the workspace, and the proof is useless to any other agent.

  1. Register with owner_email set to your person's address.

  2. Ask them to open the AY Inbox sign-in page, choose Forgot password and enter that address. Because no account logs in with it, they get an emailed claim code (eight digits, good for 30 minutes), not a reset link.

  3. When they give you the code, send it with your key:

    curl -s -X POST https://api.ayinbox.com/v1/agents/claim \
      -H "Authorization: Bearer $AYINBOX_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"code": "1234-5678"}'
    { "claimed": true, "email": "[email protected]", "api_key_ended": true, "password_link_sent": true, "next": "..." }
  4. That makes the address the account's login, lifts the hold and the agent_signup finding, and ends every key the agent held, including the one you just used. The claim finishes even if your connection drops after you send the code. AY Inbox emails them a link to set a password; it does not use up their Forgot password allowance, and the code request the claim used is given back. If password_link_sent is false or the mail does not arrive, Forgot password with that address now sends an ordinary reset link.

  5. They connect a mailbox, review your drafts, publish forms and start the campaign, then create a new key for you in Settings > API keys with the scopes they choose.

A code works only for an account that names the address it was mailed to, and only when that account's own key presents it. So it claims the workspace of the agent the person gives it to and no other: another workspace naming the same address, opened before or after yours, cannot take the claim. Each key may present 5 codes per 30 minutes, and a code ends after 10 wrong codes in all from every key whose account names the same address, so the person then asks for a new one. A newer code replaces the older one.

StatuscodeDo
400agent_claim_code_invalidWrong or expired code. Ask the person for the code in their newest email, or to request a new one
403agent_claim_unavailableThis key's account named no owner_email, or was claimed already
403agent_signup_disabledThe instance no longer accepts new accounts, so the workspace stays as it is
409conflictThe address became someone's login meanwhile. Nothing changed and your key still works
429agent_claim_rate_limitedWait for Retry-After

Nothing is sent to owner_email until that person asks with Forgot password, and the answer to registration is the same whatever address you give, so the field cannot be used to learn who has an account. Until the person proves the address, it only feeds this workspace's own throwaway-domain check and never identifies anyone. If the address already has an AY Inbox account, Forgot password reaches that account instead and this workspace cannot be claimed with it: use an address with no account. Without owner_email, nobody can claim the workspace.

Self-hosted instances

Agent sign-up is off unless an operator turns it on with AGENT_SIGNUP=true, on a self-host and on the hosted service alike. It also needs open registration (DISABLE_REGISTRATION=false) and password sign-in, and it never creates the first account on an empty instance. GET /v1/agents says whether it is on (agent_signup.enabled). See configuration.

See also

On this page