MCP server
Connect Claude Code, Claude Desktop, Cursor, ChatGPT or any MCP client to AY Inbox with an OAuth sign-in or a scoped API key, and use your data as tools.
AY Inbox speaks the Model Context Protocol. Point any MCP client at AY Inbox's server and it can search contacts, check campaigns, read your inbox, draft replies, create draft campaigns and automations, and find and verify leads, all scoped to what you grant it.
- URL:
https://api.ayinbox.com/v1/mcp - Transport: streamable HTTP
- Auth: an OAuth 2.1 access token (browser sign-in) or a static API key (bearer header).
There are two ways to connect. OAuth is the one-command path: you run a single add command, approve access in the browser, and the client holds a short-lived token it can refresh. An API key is the static path: you mint a key and paste it into the client config. Both reach the exact same tools, gated the exact same way.
An agent with no account and no person at hand can open its own workspace first: POST /v1/agents/register answers with a key, then it connects with that key as below. See For agents.
Connect with OAuth
This is the recommended path. You do not paste a key anywhere: the client discovers AY Inbox's authorization server, opens a browser for you to sign in and approve the scopes, and stores the resulting token itself.
Claude Code
claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcpThen run /mcp inside Claude Code and pick Authenticate. A browser opens, you sign in to AY Inbox, approve the requested scopes, and you are connected. No header, no key.
Claude Desktop and Cursor
Add the server with just its URL, and the client runs the OAuth flow on first use:
{
"mcpServers": {
"ayinbox": { "type": "http", "url": "https://api.ayinbox.com/v1/mcp" }
}
}ChatGPT and other clients
Any client that supports remote MCP servers over streamable HTTP, with OAuth discovery and dynamic client registration, connects the same way: give it https://api.ayinbox.com/v1/mcp and sign in when it opens the browser. In ChatGPT that means adding the URL as a custom connector, where your ChatGPT plan offers one. Client settings screens change, so follow the client's own instructions for where the URL goes; what AY Inbox serves is described in how OAuth discovery works. For function calling without MCP, see OpenAI-compatible agents.
What you are approving
The consent screen marks a self-registered client as Not affiliated with AY Inbox, names where approving sends access, and shows exactly which scopes the client will get: the ones it asks for that your role in the workspace covers, so a connected client never does more than you could yourself. Sending real mail is never one of them: SEND_CAMPAIGNS and API-key management are excluded from OAuth-connected MCP clients by design, and the assistant only ever drafts. You can revoke a connected client at any time from your dashboard under connected apps.
Connect with an API key
Prefer a static, long-lived credential (CI, a shared script, a client that does not implement OAuth)? Mint an API key and send it as a bearer token:
Authorization: Bearer aybox_...Scope your key
Create a dedicated API key for each MCP client and grant it only the scopes it needs (see Permissions). For a model that reads your inbox, start from the Agent: read and draft preset, which reads everything and drafts with AI credits but holds no scope that writes or sends. Add AI_AGENT if it should search the web or read your playbooks.
Claude Code
claude mcp add --transport http ayinbox https://api.ayinbox.com/v1/mcp \
--header "Authorization: Bearer aybox_..."Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"ayinbox": {
"type": "http",
"url": "https://api.ayinbox.com/v1/mcp",
"headers": { "Authorization": "Bearer aybox_..." }
}
}
}Cursor
Add to .cursor/mcp.json:
{
"mcpServers": {
"ayinbox": {
"url": "https://api.ayinbox.com/v1/mcp",
"headers": { "Authorization": "Bearer aybox_..." }
}
}
}What is exposed
The server exposes exactly the tools your credential is allowed to use, whether that credential is an OAuth token or an API key. Each tool checks its own scope, and the member behind the credential also needs the matching organization permission. A credential with only read scopes sees only the read tools; one with write scopes sees those too, so an MCP client acts with precisely the permissions you granted, and nothing more.
This is a subset of what the in-product assistant can do. Over MCP and the REST tools there is no person in the loop, so these rules hold for every tool:
- No tool puts mail on the wire. Tools that send a message to a recipient (
send_reply,compose_email,run_placement_test,run_placement_batch) are never listed or callable here, whatever the scope. A key withWRITE_UNIBOXcan still send throughPOST /unibox/replyover REST; a model is safer on a key that holds no write or send scope, like the agent preset below. - Booking a meeting is a send. The scheduling provider mails an invitation to a real person, so
book_meetingexists only in the assistant, where it always asks, and an MCP client books withPOST /booking/offers/:id/bookand its key. The booking credential has no tool at all, because a secret never travels in a tool's arguments. - No tool lifts a person's stop or opens a lasting way out.
remove_suppression(it can undo an unsubscribe or a complaint),create_webhook,update_webhookandrotate_webhook_secret(a stream of workspace events to any URL, or a secret in a transcript) stay with the dashboard assistant, which asks a person each time.fetch_urlstays there too: an MCP client brings its own web tools. - A tool that starts mail needs
SEND_CAMPAIGNS.add_segment_to_campaignandset_campaign_segments(with segments) into a campaign that is sending,set_automation_enabledwithenabled: true, andupdate_automationwhen it turns one on or changes the trigger of one that is on, all takeSEND_CAMPAIGNS(andsend_campaignsfor the member) on a key, the same rulefind_leadsfollows. So doupdate_segmentwhen it changes the conditions or match mode, andset_segment_memberswithincludeorauto, on a segment that feeds a sending campaign, because a wider audience is mailed straight away. Turning an automation off, enrolling into a draft or paused campaign, renaming a segment, and pinning contacts out of one do not. - Contact writes feed linked segments. Once a segment is linked to a sending campaign, a contact that comes to match it (through
add_contact,update_contact_fields,add_tag,bulk_edit_contactsor an import) is enrolled and mailed, exactly as on REST. Those tools takeWRITE_CONTACTSonly, so linking the segment is the decision that needsSEND_CAMPAIGNS. - No tool writes what the AI reads as instructions. The workspace's product description, ideal customer notes and voice, and its playbooks, are read by every AI writing surface, so no tool changes them. A key with
WRITE_WORKSPACEcan set the first three overPATCH /organization/current. - No tool approves a first email or records consent. Those are a person's decisions; their REST routes stay for your own code.
- Workspace governance stays with a person. Team, invitations and roles writes, workspace settings writes and billing are not tools.
tools/list returns only the tools your mask allows. The paid Looot company lookups (company_website, company_news, company_linkedin_posts) are not exposed over MCP either; only contact research and the assistant run them.
| Tool | Does | Required scope |
|---|---|---|
search_contacts | Find contacts by text | READ_CONTACTS |
get_contact | Read one contact | READ_CONTACTS |
update_contact_fields | Update contact fields | WRITE_CONTACTS |
add_tag / remove_tag | Add or remove a label on a contact | WRITE_CONTACTS |
list_campaigns | List campaigns | READ_CAMPAIGNS |
get_campaign_stats | Campaign stats, all time or for the emails sent from from to to | READ_ANALYTICS |
list_campaign_leads | A campaign's leads with derived status + totals | READ_CONTACTS |
create_campaign_draft | Create a draft campaign | WRITE_CAMPAIGNS |
set_campaign_status | Start or stop a campaign | SEND_CAMPAIGNS |
set_lead_hold | Hold or resume one contact's flow in one campaign, without unsubscribing them | WRITE_CAMPAIGNS |
get_dashboard_analytics | Sending stats for a period, org-wide or for chosen campaigns and campaign folders | READ_ANALYTICS |
list_mailboxes | Sender mailboxes with health + warmup state | READ_ANALYTICS |
list_recommendations / get_advisor_summary | The Advisor's open findings with their evidence, filtered by area, category or one campaign, mailbox or step, and the workspace's Advisor health score with counts by severity | READ_ANALYTICS |
list_placement_tests / get_placement_test | Inbox placement tests and where each copy landed | READ_ANALYTICS |
list_placement_batches / get_placement_batch | Placement batches, their progress, and placement by sending domain, sending provider and recipient provider | READ_ANALYTICS |
add_contact / delete_contact | Create or delete a contact | WRITE_CONTACTS |
bulk_edit_contacts | Label or subscribe many contacts at once | BULK_CONTACTS |
find_leads | Start a Looot lead job: find people at company domains, verify them, and import the valid ones, within max_cost_usd | INTEGRATIONS and BULK_CONTACTS, plus WRITE_CAMPAIGNS with campaign_id, and SEND_CAMPAIGNS when that campaign is sending |
get_lead_job / list_lead_jobs | A lead job's progress and spend, or the latest jobs | INTEGRATIONS |
verify_email | Check one address through the workspace's verifier, saving nothing | WRITE_CONTACTS |
get_contact_timeline / get_contact_sent_emails | Read a contact's activity and sent mail | READ_CONTACTS |
list_segments / get_segment / list_segment_fields | Read saved audiences, and the fields a condition can name | READ_CONTACTS |
preview_segment | Count what a set of conditions would match, saving nothing | READ_CONTACTS |
create_segment / update_segment / delete_segment | Manage saved audiences | WRITE_CONTACTS, plus SEND_CAMPAIGNS to change who is in a segment that feeds a sending campaign |
set_segment_members | Pin contacts into or out of a segment | WRITE_CONTACTS, plus SEND_CAMPAIGNS to pin contacts in to a segment that feeds a sending campaign |
list_campaign_segments | Read the segments feeding a campaign | READ_CAMPAIGNS |
set_campaign_segments / add_segment_to_campaign | Link an audience to a campaign, or enrol its members once | WRITE_CAMPAIGNS, plus SEND_CAMPAIGNS into a sending campaign |
list_forms / get_form / list_form_submissions / get_form_stats | Read hosted forms, their submissions, and their funnel | READ_CONTACTS |
create_form / update_form / delete_form | Manage hosted forms | WRITE_CONTACTS |
mint_form_link | Get one contact's personalized form URL | WRITE_CONTACTS |
get_campaign_forms | How a campaign's forms performed for its recipients | READ_CAMPAIGNS |
list_suppressions | Read the do-not-contact list, and why each entry is on it | READ_CONTACTS |
add_suppressions | Suppress an address or domain | WRITE_CONTACTS |
list_pipelines / list_deals / get_deal / list_deals_by_contact | Read CRM pipelines and deals | READ_CRM |
create_task / create_deal | Create CRM records | WRITE_CRM |
update_deal / move_deal_stage / delete_deal | Edit, move, or delete a deal | WRITE_CRM |
list_tasks / list_task_types | Read CRM tasks and task types | READ_CRM |
update_task / complete_task / delete_task | Edit, complete, or delete a task | WRITE_CRM |
add_contact_note / list_contact_notes | Add or read contact notes | WRITE_CRM / READ_CRM |
update_contact_note / delete_contact_note | Edit or delete a note | WRITE_CRM |
create_pipeline / update_pipeline / delete_pipeline | Manage CRM pipelines | WRITE_CRM |
create_pipeline_stage / update_pipeline_stage / delete_pipeline_stage | Manage pipeline stages | WRITE_CRM |
get_campaign / list_campaign_senders / get_campaign_logs | Read a campaign's config, senders, and logs | READ_CAMPAIGNS |
update_campaign / delete_campaign | Edit or delete a campaign | WRITE_CAMPAIGNS |
set_campaign_senders / verify_campaign_tracking_domain | Set senders, verify tracking domain | WRITE_CAMPAIGNS |
list_campaign_steps | Read a campaign's sequence steps | READ_CAMPAIGNS |
add_campaign_step / update_campaign_step / delete_campaign_step | Edit a campaign's sequence steps | WRITE_CAMPAIGNS |
list_threads / get_thread | Read the unified inbox | READ_UNIBOX |
get_inbox_overview / get_unread_count | The inbox at a glance, and unread counts | READ_UNIBOX |
generate_reply_draft | Have the AI write a reply grounded on the thread, the contact and the campaign brain that applies, else your voice. The result carries grounding when a brain applied. Charges AI credits; never sends. idempotency_key is required | READ_UNIBOX and AI_DRAFT |
draft_reply | Package reply text you wrote with the thread's recipient and subject, checked against the campaign brain: the result carries flags, or check_unavailable when the check could not run. Calls no model and never sends | READ_UNIBOX |
list_agent_drafts | The inbox agent's suggested replies waiting for a person | READ_UNIBOX |
discard_agent_draft | Discard one of those drafts so it is never sent | WRITE_UNIBOX |
list_brains / get_brain / preview_brain | Read campaign brains: public facts, do-not-say phrases and source titles, or what a reply would read for a sample. get_brain takes id, campaign_id or thread_id; with a campaign or thread it adds applied, the merged values a draft reads with where each comes from (campaign, default, campaign_and_default or voice_profile). Never internal rules or internal material | READ_CAMPAIGNS |
get_brain_material | Read one public source's text, fenced and in pages (offset, next_offset) | READ_CAMPAIGNS |
get_brain_internal | Read a brain's internal rules, one internal source (material_id) or one internal page (page_id). Write-risk, so the assistant always asks | WRITE_CAMPAIGNS |
list_brain_pages / get_brain_page | List a brain's pages, or read one public page by page_id or path with the pages it links to and the pages linking to it, fenced. Never an internal page | READ_CAMPAIGNS |
write_brain_page | Create a page at a path (or from its title and type), or replace the page already there. Internal unless visibility is public. In the assistant, it always asks | WRITE_CAMPAIGNS |
create_brain / update_brain / delete_brain | Manage brains; editing or deleting the workspace default also needs manage_settings for the key's member. In the assistant, update_brain and delete_brain always ask | WRITE_CAMPAIGNS |
add_brain_material / update_brain_material / remove_brain_material / refresh_brain_material | Add pasted text or a public page, pin it, mark it OK to quote, remove or refetch it. Files are a REST upload. In the assistant, each always asks | WRITE_CAMPAIGNS |
set_campaign_brain / set_default_brain | Choose a campaign's brain, or the workspace default (manage_settings) | WRITE_CAMPAIGNS |
get_booking_settings / get_booking_summary | Read the booking mode, follow-up bounds and counts | READ_UNIBOX / READ_CONTACTS |
update_booking_settings | Set booking to off or draft, and the follow-up bounds | INTEGRATIONS |
enable_booking_auto | Turn on automatic booking, or see what it needs. Always asks and needs a recent confirmation | SEND_CAMPAIGNS |
list_booking_types / create_booking_type / update_booking_type / delete_booking_type | Read or manage booking types | READ_CONTACTS / INTEGRATIONS |
list_booking_event_types | A connection's event types and what blocks each one | INTEGRATIONS |
get_availability | Open times in the prospect's timezone | READ_CONTACTS, plus READ_UNIBOX with a thread |
list_meeting_offers / get_meeting_offer | Read offers with their times and decision log | READ_UNIBOX |
offer_meeting_times | Prepare a block of times for a reply; sends nothing | WRITE_UNIBOX |
stop_meeting_offer / resume_meeting_offer / reject_meeting_pick | Stop, resume or drop a pick | WRITE_UNIBOX |
get_auto_reply_settings | Read the workspace's auto-reply switch and whether this instance can run it | READ_UNIBOX |
pause_auto_reply | Stop every automatic answer in the workspace now | WRITE_UNIBOX |
enable_auto_reply | Turn on the workspace auto-reply switch. In the assistant it always asks and needs a recent confirmation | SEND_CAMPAIGNS |
get_campaign_auto_reply | Read a campaign's auto-reply mode, approved links and figures, checklist and results | READ_CAMPAIGNS |
update_campaign_auto_reply | Replace a campaign's auto-reply settings while it is off or Supervised (a field left out returns to its default), or lower an automatic campaign | WRITE_CAMPAIGNS |
enable_campaign_auto_reply | Set a campaign to answer automatically with exactly the settings given. Always asks and needs a recent confirmation | SEND_CAMPAIGNS |
list_auto_reply_runs / get_auto_reply_run | Read what auto-reply did and why; the answer's body is returned fenced as data | READ_UNIBOX |
cancel_auto_reply_run | Stop one automatic answer before it sends | WRITE_UNIBOX |
list_scheduled_sends | List queued outbound messages | READ_UNIBOX |
mark_thread_seen / set_thread_labels / move_thread_folder | Mark seen, set thread labels, move threads to a folder | WRITE_UNIBOX |
snooze_thread / unsnooze_thread / cancel_scheduled_send | Snooze a thread, cancel a queued send | WRITE_UNIBOX |
get_mailbox / get_warmup_ban_status | Read a mailbox's config and warmup standing | READ_EMAILS |
update_mailbox / set_mailbox_warmup / set_mailbox_send_hold / set_mailbox_tracking_domain | Edit mailbox settings, warmup, the campaign hold, tracking domain | WRITE_EMAILS |
submit_warmup_appeal / disconnect_mailbox | Appeal a warmup ban, disconnect a mailbox | WRITE_EMAILS |
create_automation_draft / list_automations / get_automation | Read automations, create a disabled draft | INTEGRATIONS |
update_automation / set_automation_enabled / delete_automation | Edit, enable, or delete an automation | INTEGRATIONS, plus SEND_CAMPAIGNS to turn one on |
get_org_settings / get_workspace_limits | The workspace profile and what the AI is grounded on, and plan limits next to usage | READ_WORKSPACE |
get_subscription / get_subscription_limits | The plan, status and trial, without billing identifiers | READ_WORKSPACE |
get_credit_balance / get_credit_usage / list_credit_transactions | AI credits: balance, spend by day and feature, and the ledger | READ_WORKSPACE, and manage_billing for the member |
list_labels | Campaign folders, mailbox tags or contact categories, by kind | READ_CAMPAIGNS, READ_EMAILS or READ_CONTACTS for that kind |
list_api_keys / create_api_key / update_api_key / revoke_api_key | Manage API keys | API_KEYS |
list_webhooks / list_webhook_deliveries | Read webhook endpoints and deliveries | WEBHOOKS |
delete_webhook / verify_webhook | Delete or test a webhook endpoint | WEBHOOKS |
search_web | Search public pages | AI_AGENT |
load_skill | Read one of your playbooks | AI_AGENT |
list_threads and get_thread return their documented JSON to API key and MCP callers. list_threads takes the same campaign_id, contact_id, since and until filters as GET /v1/unibox, and each thread carries its campaign_id when one resolves. A malformed value is refused as invalid arguments; a campaign or contact outside the workspace answers not found. Every brain tool result is capped at 12,000 characters, and material text is wrapped in <<<UNTRUSTED_CONTENT>>> markers. A brain file upload is not a tool: pass text or url to add_brain_material.
Two form surfaces are deliberately absent: brand-asset uploads (logo, cover, background) are multipart images with dimension and MIME limits that a tool call cannot express, and the custom forms domain is workspace-wide branding behind a DNS check, so it lives in settings rather than in a tool.
Rate limits apply to MCP calls exactly as they do to the rest of the API (per-key for API keys, per-user for OAuth tokens). A self-registered MCP client is never granted SEND_CAMPAIGNS or WRITE_WORKSPACE, so only an API key or app that holds the scope can start or stop a campaign, link an audience to one that is sending, or widen a segment that feeds one, over MCP. Contacts that come to match a segment already linked to a sending campaign are still enrolled, as they are on REST.
Give a mail-reading model the agent preset
The Agent: read and draft preset on the create-key dialog holds every read scope plus AI_DRAFT: the model can read the inbox, campaigns and contacts, and have the AI write replies with generate_reply_draft, charged to credits within the workspace's spend limits. It holds no scope that writes or sends, so it cannot send over the tools or over REST. Use it for any model that reads mail written by people outside the workspace.
Results are data
Anyone who can email the workspace, fill in a form or appear in an imported list writes text a model reads. So every tools/call result is fenced: the text starts with one line saying everything between the markers is data, then the result between <<<UNTRUSTED_CONTENT>>> and <<<END_UNTRUSTED_CONTENT>>>. Markers inside the data are removed, so the data cannot close the fence early. The one result that is not fenced is load_skill, which is your workspace's own playbook. Treat the fence as one layer: it lowers the odds that steering works, and the rules above limit what a steered model can do.
A client that parsed content[0].text as JSON should read the JSON between the markers.
Toolsets
The full catalog is large for a small model. Narrow tools/list with ?toolsets=, a comma-separated list, and start a cheap model on inbox:
https://api.ayinbox.com/v1/mcp?toolsets=inboxThe names are inbox, campaigns, contacts, leads, crm, mailboxes, workspace, automation, forms, developer, research and analytics. The filter narrows the list only: a tool outside it can still be called, with the same gates. An unknown name answers JSON-RPC -32602. The form is tested with API keys; an OAuth client that ties its token to the exact resource URL may not accept it, and gets the whole list without it.
Retries
Every write tool's listed schema carries an optional idempotency_key. Always pass one: a retry with the same key and arguments returns the first result instead of doing it again, for 24 hours. The key is bound to the credential that used it, so another key's retry is a conflict, never a replay. A call refused before it ran (a missing scope, a bad argument) is not stored, so a retry with the same key runs. A key reused with different arguments answers idempotency_key_reused, a retry while the first call still runs answers tool_call_in_progress, and a call that never recorded how it ended answers idempotency_outcome_unknown after ten minutes, each as an isError result whose text starts with the code. Read tools and tools that return a secret store nothing.
find_leads and generate_reply_draft take the key in their own arguments and dedupe it themselves: a generate_reply_draft key pays for one draft. A retry with a key whose draft succeeded is refused, uncharged and with no second draft, so pass a new key for a new draft. A retry after the model failed (which gives the credits back) is charged again and writes the draft. A call with no key answers idempotency_key_required. No draft text is stored.
No tool on these surfaces returns a secret: create_api_key points you to the dashboard, and the webhook secret tools stay with the assistant, which shows a new secret to the member once and never sees it.
Find and verify leads
The lead tools let an agent build a list end to end: start a lead job, follow it, and check single addresses before it adds them anywhere.
find_leads,get_lead_jobandlist_lead_jobsappear only when the instance has turned on Looot lead jobs (AYINBOX_LOOOT_LEADS). Starting a job takes whatPOST /integrations/looot/lead-jobstakes: a paid plan,INTEGRATIONSandBULK_CONTACTS, and for the member behind the credentialuse_integrationsandmanage_contacts. Reading jobs takes what theGETroutes take:INTEGRATIONS, and for the member eitheruse_integrationsormanage_settings.find_leadsrequiresdomainsandmax_cost_usd. There is no approval step over MCP, so the budget is the only limit on what a call spends from the instance's Looot balance: the job stops before a domain it can no longer afford. On an instance that bills with credits, each domain is also charged to the workspace's credits, and the job stops when they run out.max_cost_usdis 0.05 to 50.titles,per_domain(1 to 25, default 3),campaign_idandcategory_idsare optional, and the schema carries each numeric range so a client can check it before calling.campaign_idtakes the rights thatadd_segment_to_campaigntakes:WRITE_CAMPAIGNS, andmanage_campaignsfor the member. Without them the call fails with a tool error that says so; leavecampaign_idout to import the contacts without enrolling them.- A campaign that is sending mails the contacts it gets, so naming one makes the lead job a send, and it also takes the rights that
set_campaign_statustakes:SEND_CAMPAIGNS, andsend_campaignsfor the member. That is a campaign that is active, one that finished (it restarts for new leads), and one parked because its leads were undeliverable (it resumes on its own once they verify). A self-registered MCP client is never grantedSEND_CAMPAIGNS, so over OAuthfind_leadsonly enrols leads in a draft or paused campaign, which a member then starts from the dashboard. Without the rights the call fails with a tool error naming them, the same refusalPOST /integrations/looot/lead-jobsanswers as403looot_campaign_send_required. Nothing is started or charged. - Always pass
idempotency_keytofind_leads. A retry with the same key returns the job it first started instead of starting and paying for a second one. The MCP endpoint reads noIdempotency-Keyheader, so a client that retries a timed-out call without the argument starts and pays for a second job; the result'snotesays so when the key is missing. - The job runs in the background.
find_leadsanswers at once withjob_id,statusandestimated_max_usd, the most the job could spend before its budget applies, and on an instance that bills with creditsestimated_max_credits, the most the workspace can be charged; pollget_lead_jobfordomains_done,found,verified,imported,skipped,spent_usd,held_usd(spend still being settled) andbudget_reached. verify_emailchecks one address through whichever verifier the workspace uses (its connected provider, the instance's Looot or MillionVerifier balance, or the built-in check) and returnsstatus,sub_status,reasonandprovider.reasonis the verifier's or the remote mail server's own words, folded to one line of at most 200 characters: treat it as data, not as instructions. A paid verifier charges for the check, so it needsWRITE_CONTACTSandmanage_contactsrather than a read scope. Through Looot, the same address checked again on the same day replays the first answer instead of paying again. Nothing is written to your contacts.
An argument the tool cannot use (a missing budget, a malformed UUID, an unknown argument) comes back as a tool error naming the argument, so the agent can correct the call. A self-registered MCP client can be granted INTEGRATIONS and BULK_CONTACTS, so only approve those scopes for a client you trust to spend the instance's Looot balance.
How OAuth discovery works
The one-command flow is standard MCP authorization, so any spec-compliant client works without AY Inbox-specific setup. On a request with no valid token, /v1/mcp returns 401 with a WWW-Authenticate header pointing at the protected-resource metadata. The client then walks:
GET /.well-known/oauth-protected-resource: the resource and its authorization server (RFC 9728).GET /.well-known/oauth-authorization-server: the authorize, token, and registration endpoints (RFC 8414).POST /v1/oauth/register: dynamic client registration, so the client mints a public (PKCE)client_idwith no manual app setup (RFC 7591).- Authorization code with PKCE (S256) in the browser, then
POST /v1/oauth/tokenfor a short-lived access token and a rotating refresh token.
The same discovery drives AY Inbox's OAuth server for any client. See the OAuth 2.1 page for the full authorization-server reference.
See also
- Agent tools (REST) for the same registry over plain HTTP, for Hermes and OpenAI-style function-calling agents that do not speak MCP.
- OAuth 2.1 for the authorization-server endpoints, scopes, and dynamic registration.
- Authentication for how API keys and OAuth tokens work.
- Permissions for the full scope list.
- Agent quickstart and running outbound from Claude Code for a first session end to end.
- To connect AY Inbox to external MCP servers instead (the other direction), see the Connect MCP tools guide.
Endpoint scope map
Every AY Inbox API endpoint with the auth types it accepts and the permission it requires, so you know exactly what an API key or OAuth token can reach.
Agent tools (REST)
Use the AY Inbox AI tool registry from any function-calling agent over plain HTTP: list the tools a key allows, export OpenAI or Hermes manifests, run tools.