# Campaign Commander Agent API and MCP > Political operations platform. One organization credential, bound to the organization that owns it, drives the MCP server. Connect to MCP with OAuth 2.1 (the supported self-service flow) or an existing API key in a compatible secret store; the versioned REST Agent API is API-key-only and an OAuth access token is not a direct versioned-API credential. This is a Campaign Commander integration document, not a published industry standard. ## Connect - Give your agent this documentation link. A host that can make HTTP requests and can add an MCP connection with OAuth can read this document and start a connection; you do not need to manually mint an API key, and reading this document requires no JavaScript, cookies, login, screenshot, private vendor instruction, or document read key. A documentation link cannot give a host a capability or connection UI it lacks; it must report that limitation rather than claim to connect. - Connect with OAuth for the supported self-service MCP flow: this server publishes protected-resource metadata at the MCP origin, supports dynamic client registration, and requires the authorization-code grant with PKCE S256 (a public client, token_endpoint_auth_method=none). The user signs in, chooses the organization the credential is bound to, and approves the requested scopes on a consent screen. No manually supplied client secret is required. - A host that cannot present an API key and cannot run this OAuth flow must report that constraint; do not look for an API-key field the host does not offer, and do not treat a supplied screenshot or vendor-specific private instruction as a requirement of this protocol. Some hosts require their own connection UI. - Send exactly one JSON-RPC message per POST to https://www.campaigncommanderapp.com/api/mcp. JSON-RPC batch arrays are not supported, including singleton arrays and requests combined with notifications. The server is stateless: GET and DELETE return 405. - After the host performs its token handling, initialize, complete the normal SDK initialization notification if applicable, then call tools/list, then call the get_capabilities tool with {}. It reads the real granted scopes, the organization binding, and whether setup is complete; never fabricate confirmation. - Direct REST access is API-key-only: an OAuth access token is an MCP credential and is not a direct credential for the versioned API. Read the public OpenAPI document at https://www.campaigncommanderapp.com/openapi.json for the full HTTP surface. ## Capability discovery Call the `get_capabilities` MCP tool (or `GET https://www.campaigncommanderapp.com/api/v1/agent/capabilities`) first. It returns the granted scopes, the organization the credential acts in, and whether setup is complete. It changes nothing and works even before setup is finished. ## Tools 134 MCP tools each delegate to a real Agent API operation in-process, so authentication, scopes, tenant isolation, audit, idempotency, billing, suppression, and consent stay where they already are. - `get_capabilities`: read-only; scopes: any valid credential - `list_contacts`: read-only; scopes: contacts:read - `get_contact`: read-only; scopes: contacts:read - `list_segments`: read-only; scopes: lists:read - `create_email_draft`: writes; scopes: email:draft - `create_contact`: writes; scopes: contacts:write - `update_contact`: writes; scopes: contacts:write - `add_contact_tags`: writes; scopes: contacts:write - `remove_contact_tags`: writes; scopes: contacts:write - `list_suppressions`: read-only; scopes: suppression:read - `add_suppression`: writes; scopes: suppression:write - `preview_email_audience`: read-only; scopes: email:draft - `get_email_draft`: read-only; scopes: email:read - `update_email_draft`: writes; scopes: email:draft - `send_email_campaign`: writes; requires confirm + idempotency_key; scopes: email:send - `create_segment`: writes; scopes: lists:write - `get_segment`: read-only; scopes: lists:read - `preview_list`: read-only; scopes: lists:read - `change_segment_members`: writes; scopes: lists:write - `resolve_list`: read-only; scopes: lists:read - `get_segment_usage`: read-only; scopes: lists:read - `set_contact_consent`: writes; scopes: contacts:write - `restore_suppression`: writes; scopes: suppression:write - `override_suppression`: writes; scopes: suppression:write - `list_email_campaigns`: read-only; scopes: email:read - `test_email_campaign`: writes; requires confirm + idempotency_key; scopes: email:test - `list_test_recipients`: read-only; scopes: email:test - `add_test_recipient`: writes; scopes: email:test - `remove_test_recipient`: writes; scopes: email:test - `schedule_email_campaign`: writes; requires confirm + idempotency_key; scopes: email:send - `unschedule_email_campaign`: writes; requires confirm + idempotency_key; scopes: email:send - `list_email_recipients`: read-only; scopes: logs:read - `list_email_events`: read-only; scopes: logs:read - `set_email_audience`: writes; scopes: email:draft - `refresh_email_recipients`: writes; scopes: email:draft - `get_email`: read-only; scopes: email:transactional - `list_emails`: read-only; scopes: email:transactional - `list_sms_campaigns`: read-only; scopes: sms:read - `get_sms_campaign`: read-only; scopes: sms:read - `create_sms_draft`: writes; scopes: sms:draft - `update_sms_draft`: writes; scopes: sms:draft - `set_sms_audience`: writes; scopes: sms:draft - `preview_sms_audience`: read-only; scopes: sms:draft - `test_sms_campaign`: writes; requires confirm + idempotency_key; scopes: sms:test - `send_sms_campaign`: writes; requires confirm + idempotency_key; scopes: sms:send - `schedule_sms_campaign`: writes; requires confirm + idempotency_key; scopes: sms:send - `unschedule_sms_campaign`: writes; requires confirm + idempotency_key; scopes: sms:send - `list_sms_recipients`: read-only; scopes: sms:read, logs:read - `list_sms_events`: read-only; scopes: sms:read, logs:read - `get_sms_outcomes`: read-only; scopes: sms:read, logs:read - `get_sms_readiness`: read-only; scopes: sms:read - `list_mail_campaigns`: read-only; scopes: mail:read - `get_mail_campaign`: read-only; scopes: mail:read - `create_mail_draft`: writes; requires confirm + idempotency_key; scopes: mail:draft - `preview_mail_audience`: read-only; scopes: mail:read - `launch_mail_campaign`: writes; requires confirm + idempotency_key; scopes: mail:send - `list_voice_campaigns`: read-only; scopes: voice:read - `get_voice_campaign`: read-only; scopes: voice:read - `create_voice_draft`: writes; scopes: voice:draft - `launch_voice_campaign`: writes; requires confirm + idempotency_key; scopes: voice:send - `list_voice_recipients`: read-only; scopes: voice:read, logs:read - `start_voice_call`: writes; requires confirm + idempotency_key; scopes: voice:send - `get_voice_call`: read-only; scopes: voice:read - `list_voice_numbers`: read-only; scopes: voice:read - `upload_voice_asset`: writes; scopes: voice:draft - `list_dialer_sessions`: read-only; scopes: voice:read - `get_dialer_session`: read-only; scopes: voice:read - `advance_dialer_session`: writes; requires confirm + idempotency_key; scopes: voice:send - `get_active_call`: read-only; scopes: voice:read - `start_dialer_session`: writes; scopes: voice:draft - `verify_emails`: writes; requires confirm + idempotency_key; scopes: email:verify - `verify_addresses`: writes; requires confirm + idempotency_key; scopes: address:verify - `list_email_templates`: read-only; scopes: email:transactional - `get_email_template`: read-only; scopes: email:transactional - `create_email_template`: writes; scopes: email:transactional - `update_email_template`: writes; scopes: email:transactional - `delete_email_template`: writes; scopes: email:transactional - `send_transactional_email`: writes; requires confirm + idempotency_key; scopes: email:transactional - `list_transactional_emails`: read-only; scopes: email:transactional - `get_transactional_email`: read-only; scopes: email:transactional - `list_audit_log`: read-only; scopes: logs:read - `list_inbox_conversations`: read-only; scopes: inbox:read - `get_inbox_conversation`: read-only; scopes: inbox:read - `export_contacts`: read-only; scopes: exports:read - `get_communications_settings`: read-only; scopes: settings:read - `get_organization_settings`: read-only; scopes: settings:read - `update_organization_settings`: writes; scopes: settings:write - `add_conversion`: writes; scopes: conversions:write - `list_agent_runs`: read-only; scopes: agent:runs - `get_agent_run`: read-only; scopes: agent:runs - `get_texting_brand_registration`: read-only; scopes: registration:read - `preview_texting_brand_registration`: read-only; scopes: registration:read - `request_texting_brand_approval`: writes; scopes: registration:submit - `submit_texting_brand_registration`: writes; requires confirm + idempotency_key; scopes: registration:submit - `get_sending_domain`: read-only; scopes: settings:read - `preview_sending_domain`: read-only; scopes: settings:read - `configure_sending_domain`: writes; scopes: settings:write - `list_my_calling_campaigns`: read-only; scopes: calls:read - `update_my_calling_phone`: writes; scopes: calls:operate - `get_personal_settings`: read-only; scopes: profile:read - `update_personal_settings`: writes; scopes: profile:write - `get_texting_campaign_registration`: read-only; scopes: registration:read - `check_texting_campaign_site`: writes; scopes: registration:prepare - `save_texting_campaign_draft`: writes; scopes: registration:prepare - `submit_texting_campaign_registration`: writes; requires confirm + idempotency_key; scopes: registration:submit - `get_billing_balance`: read-only; scopes: billing:read - `list_billing_transactions`: read-only; scopes: billing:read - `get_auto_topup_settings`: read-only; scopes: billing:read - `update_auto_topup_settings`: writes; scopes: billing:write - `preview_contact_import`: writes; scopes: contacts:read - `commit_contact_import`: writes; scopes: contacts:write - `list_contact_imports`: read-only; scopes: contacts:read - `get_contact_import_outcomes`: read-only; scopes: contacts:read - `get_import_cleaning_cohort`: read-only; scopes: contacts:read - `stage_contact_import_upload`: writes; scopes: contacts:write - `clean_import_cohort`: writes; requires confirm + idempotency_key; scopes: email:verify, address:verify - `record_form_submission`: writes; scopes: intake:write - `get_form_submission`: read-only; scopes: intake:write - `list_team_members`: read-only; scopes: team:read - `list_team_invitations`: read-only; scopes: team:read - `list_contact_fields`: read-only; scopes: contacts:read - `upsert_contact_field`: writes; scopes: contacts:write - `create_team_invitation`: writes; scopes: team:manage - `resend_team_invitation`: writes; scopes: team:manage - `cancel_team_invitation`: writes; scopes: team:manage - `change_team_member_role`: writes; scopes: team:manage - `suspend_team_member`: writes; scopes: team:manage - `reactivate_team_member`: writes; scopes: team:manage - `remove_team_member`: writes; scopes: team:manage - `search_phone_numbers`: read-only; scopes: settings:read - `purchase_phone_number`: writes; requires confirm + idempotency_key; scopes: settings:write - `set_default_phone_number`: writes; scopes: settings:write - `test_voice_recording`: writes; requires confirm + idempotency_key; scopes: voice:send - `test_robocall`: writes; requires confirm + idempotency_key; scopes: voice:send ## Public API operations - `GET /api/v1/agent/capabilities`: Read scopes, endpoints, and prices for this key (scopes: any valid credential) - `GET /api/v1/agent/openapi`: Read the public API document (scopes: no credential required) - `GET /api/v1/agent/runs`: List this key's work runs (scopes: agent:runs) - `POST /api/v1/agent/runs`: Start a work run (scopes: agent:runs) - `GET /api/v1/agent/runs/{id}`: Read one work run (scopes: agent:runs) - `PATCH /api/v1/agent/runs/{id}`: Update a work run (scopes: agent:runs) - `GET /api/v1/agent/approvals`: List legacy spend approvals for this key (scopes: any valid credential) - `POST /api/v1/agent/approvals`: Request an optional legacy spend approval (scopes: any valid credential) - `GET /api/v1/communications/settings`: Read sender and compliance settings (scopes: settings:read) - `GET /api/v1/communications/sending-domain`: Read the sending domain and its DNS records (scopes: settings:read) - `POST /api/v1/communications/sending-domain`: Authenticate a sending domain (scopes: settings:write) - `POST /api/v1/communications/sending-domain/preview`: Preview a sending-domain action (scopes: settings:read) - `GET /api/v1/organization/settings`: Read organization settings (scopes: settings:read) - `POST /api/v1/organization/settings`: Save organization settings (scopes: settings:write) - `GET /api/v1/profile`: Read your own personal profile and preferences (scopes: profile:read) - `PATCH /api/v1/profile`: Save your own personal profile and preferences (scopes: profile:write) - `POST /api/v1/intake/form-submissions`: Record a website form submission as a contact (scopes: intake:write) - `GET /api/v1/intake/form-submissions/{externalId}`: Read a stored form-submission receipt (scopes: intake:write) - `GET /api/v1/calling/campaigns`: List the calling queues you may work (scopes: calls:read) - `PATCH /api/v1/calling/phone`: Save your own operator phone (scopes: calls:operate) - `GET /api/v1/team/members`: List the authorized organization's team roster (scopes: team:read) - `GET /api/v1/team/invitations`: List the authorized organization's current pending invitations (scopes: team:read) - `POST /api/v1/team/invitations`: Invite a person to the organization (scopes: team:manage) - `GET /api/v1/contacts/fields`: List the authorized organization's contact-field registry (scopes: contacts:read) - `PUT /api/v1/contacts/fields`: Save one contact-field definition (scopes: contacts:write) - `GET /api/v1/lists`: List saved audiences (scopes: lists:read) - `POST /api/v1/lists`: Create a saved list (scopes: lists:write) - `GET /api/v1/lists/{id}`: Read one saved list (scopes: lists:read) - `PATCH /api/v1/lists/{id}`: Edit a saved list (scopes: lists:write) - `DELETE /api/v1/lists/{id}`: Delete a saved list (scopes: lists:write) - `POST /api/v1/lists/{id}/members`: Add contacts to a static list (scopes: lists:write) - `DELETE /api/v1/lists/{id}/members`: Remove contacts from a static list (scopes: lists:write) - `GET /api/v1/lists/{id}/usage`: Count campaigns that use a list (scopes: lists:read) - `GET /api/v1/lists/resolve`: Resolve an audience from plain language (scopes: lists:read) - `POST /api/v1/lists/preview`: Count a prospective list (scopes: lists:read) - `GET /api/v1/contacts`: Search contacts (scopes: contacts:read) - `POST /api/v1/contacts`: Create a contact (scopes: contacts:write) - `GET /api/v1/contacts/{id}`: Read one contact (scopes: contacts:read) - `PATCH /api/v1/contacts/{id}`: Update a contact (scopes: contacts:write) - `POST /api/v1/contacts/{id}/tags`: Add tags to a contact (scopes: contacts:write) - `DELETE /api/v1/contacts/{id}/tags`: Remove tags from a contact (scopes: contacts:write) - `PATCH /api/v1/contacts/{id}/consent`: Set channel consent (scopes: contacts:write) - `POST /api/v1/contacts/import/preview`: Preview a contact or suppression import (scopes: contacts:read) - `POST /api/v1/contacts/import`: Commit a previewed import (scopes: contacts:write, email:verify, address:verify) - `GET /api/v1/contacts/imports`: List this organization's imports (scopes: contacts:read) - `GET /api/v1/contacts/imports/{id}/outcomes`: Read per-row import outcomes (scopes: contacts:read) - `GET /api/v1/contacts/imports/cohorts/{cohortId}`: Read an import identity cohort (scopes: contacts:read) - `POST /api/v1/contacts/import/cohorts/{cohortId}/clean`: Clean one import identity cohort (scopes: email:verify, address:verify) - `POST /api/v1/contacts/import/staged`: Stage a large contact-import upload in chunks (scopes: contacts:write) - `GET /api/v1/inbox/conversations`: List inbox conversations (scopes: inbox:read) - `GET /api/v1/inbox/conversations/{id}`: Read one inbox conversation (scopes: inbox:read) - `GET /api/v1/suppressions`: List suppression records (scopes: suppression:read) - `POST /api/v1/suppressions`: Record a suppression (scopes: suppression:write) - `PATCH /api/v1/suppressions/{id}`: Bypass an overridable suppression (scopes: suppression:write) - `DELETE /api/v1/suppressions/{id}`: Withdraw a suppression bypass (scopes: suppression:write) - `POST /api/v1/email/campaigns/preview`: Preview an email audience and cost (scopes: email:draft) - `GET /api/v1/email/campaigns`: List email campaigns (scopes: email:read) - `POST /api/v1/email/campaigns`: Create an email draft (scopes: email:draft) - `GET /api/v1/email/campaigns/{id}`: Read an email campaign (scopes: email:read) - `PATCH /api/v1/email/campaigns/{id}`: Revise an email draft (scopes: email:draft) - `DELETE /api/v1/email/campaigns/{id}`: Delete an unsent email draft (scopes: email:draft) - `PATCH /api/v1/email/campaigns/{id}/audience`: Replace a draft audience (scopes: email:draft) - `POST /api/v1/email/campaigns/{id}/recipients/refresh`: Rebuild a draft recipient snapshot (scopes: email:draft) - `POST /api/v1/email/campaigns/{id}/test`: Send a test of a stored email draft (scopes: email:test) - `GET /api/v1/email/test-recipients`: List test recipients (scopes: email:test) - `POST /api/v1/email/test-recipients`: Add a test recipient (scopes: email:test) - `DELETE /api/v1/email/test-recipients/{id}`: Remove a test recipient (scopes: email:test) - `POST /api/v1/email/campaigns/{id}/send`: Start a live email campaign (scopes: email:send) - `POST /api/v1/email/campaigns/{id}/schedule`: Schedule an email draft (scopes: email:send) - `POST /api/v1/email/campaigns/{id}/unschedule`: Return a scheduled email to draft (scopes: email:send) - `GET /api/v1/email/transactional`: List transactional emails (scopes: email:transactional) - `POST /api/v1/email/transactional`: Send a transactional email (scopes: email:transactional) - `GET /api/v1/email/transactional/{id}`: Read one transactional email (scopes: email:transactional) - `GET /api/v1/emails`: List transactional emails (alias) (scopes: email:transactional) - `POST /api/v1/emails`: Send a transactional email (alias) (scopes: email:transactional) - `GET /api/v1/emails/{id}`: Read one transactional email (alias) (scopes: email:transactional) - `GET /api/v1/email/transactional/templates`: List transactional templates (scopes: email:transactional) - `POST /api/v1/email/transactional/templates`: Create a transactional template (scopes: email:transactional) - `GET /api/v1/email/transactional/templates/{id}`: Read a transactional template (scopes: email:transactional) - `PATCH /api/v1/email/transactional/templates/{id}`: Update a transactional template (scopes: email:transactional) - `DELETE /api/v1/email/transactional/templates/{id}`: Delete a transactional template (scopes: email:transactional) - `GET /api/v1/email/transactional/webhooks`: List transactional webhook endpoints (scopes: email:transactional) - `POST /api/v1/email/transactional/webhooks`: Create a transactional webhook endpoint (scopes: email:transactional) - `PATCH /api/v1/email/transactional/webhooks/{id}`: Enable or disable a transactional webhook (scopes: email:transactional) - `DELETE /api/v1/email/transactional/webhooks/{id}`: Delete a transactional webhook (scopes: email:transactional) - `GET /api/v1/email/campaigns/{id}/recipients`: Read email recipient outcomes (scopes: logs:read) - `GET /api/v1/email/campaigns/{id}/events`: Read email delivery events (scopes: logs:read) - `GET /api/v1/email/campaigns/{id}/clicks`: Read email opens and grouped clicks (scopes: email:read) - `POST /api/v1/conversions`: Record an attributed conversion (scopes: conversions:write) - `GET /api/v1/conversions/sources`: List conversion sources (scopes: conversions:write) - `POST /api/v1/conversions/sources`: Create a conversion source (scopes: conversions:write) - `GET /api/v1/campaigns/{id}/report`: Read clicks, conversions, and throughput (scopes: conversions:write) - `GET /api/v1/verification/email`: Quote an email verification run (scopes: email:verify) - `POST /api/v1/verification/email`: Run email verification (scopes: email:verify) - `GET /api/v1/verification/address`: Quote a postal address verification run (scopes: address:verify) - `POST /api/v1/verification/address`: Run postal address verification (scopes: address:verify) - `GET /api/v1/sms/readiness`: Read texting readiness (scopes: sms:read) - `POST /api/v1/sms/campaigns/preview`: Preview a text audience and cost (scopes: sms:draft) - `GET /api/v1/sms/campaigns`: List text campaigns (scopes: sms:read) - `POST /api/v1/sms/campaigns`: Create a text draft (scopes: sms:draft) - `GET /api/v1/sms/campaigns/{id}`: Read one text campaign (scopes: sms:read) - `PATCH /api/v1/sms/campaigns/{id}`: Revise a text draft (scopes: sms:draft) - `PATCH /api/v1/sms/campaigns/{id}/audience`: Replace a text draft audience (scopes: sms:draft) - `POST /api/v1/sms/campaigns/{id}/test`: Send a text test (scopes: sms:test) - `POST /api/v1/sms/campaigns/{id}/send`: Start text delivery (scopes: sms:send) - `POST /api/v1/sms/campaigns/{id}/schedule`: Schedule a text draft (scopes: sms:send) - `POST /api/v1/sms/campaigns/{id}/unschedule`: Return a scheduled text to draft (scopes: sms:send) - `GET /api/v1/sms/campaigns/{id}/recipients`: Read text recipient outcomes (scopes: sms:read, logs:read) - `GET /api/v1/sms/campaigns/{id}/events`: Read text delivery events (scopes: sms:read, logs:read) - `GET /api/v1/sms/campaigns/{id}/clicks`: Read per-link text clicks (scopes: sms:read) - `GET /api/v1/sms/campaigns/{id}/outcomes`: Read text failure counts by reason (scopes: sms:read, logs:read) - `POST /api/v1/mail/campaigns/preview`: Preview a postcard or letter (scopes: mail:read) - `GET /api/v1/mail/campaigns`: List postcard and letter campaigns (scopes: mail:read) - `POST /api/v1/mail/campaigns`: Create a postcard or letter draft (scopes: mail:draft) - `GET /api/v1/mail/campaigns/{id}`: Read a postcard or letter campaign (scopes: mail:read) - `POST /api/v1/mail/campaigns/{id}/launch`: Submit postcards or letters for printing (scopes: mail:send) - `GET /api/v1/voice/numbers`: List voice caller ids (scopes: voice:read) - `GET /api/v1/voice/numbers/search`: Search available phone numbers (scopes: settings:read) - `POST /api/v1/voice/numbers/purchase`: Buy a toll-free phone number (scopes: settings:write) - `POST /api/v1/voice/numbers/{id}/default`: Set the default caller id (scopes: settings:write) - `POST /api/v1/voice/assets`: Upload a voicemail recording (scopes: voice:draft) - `GET /api/v1/voice/campaigns`: List call, voicemail, and robocall drafts (scopes: voice:read) - `POST /api/v1/voice/campaigns`: Create a call, voicemail, or robocall draft (scopes: voice:draft) - `GET /api/v1/voice/campaigns/{id}`: Read a voice campaign (scopes: voice:read) - `GET /api/v1/voice/campaigns/{id}/recipients`: Read voice recipient outcomes (scopes: voice:read, logs:read) - `POST /api/v1/voice/campaigns/{id}/launch`: Launch voicemail delivery (scopes: voice:send) - `POST /api/v1/voice/campaigns/{id}/recording-test`: Play a robocall draft recording on the key owner's registered phone (scopes: voice:send) - `POST /api/v1/voice/robocall/test`: Place a personal robocall test (scopes: voice:send) - `POST /api/v1/voice/calls`: Place a one-to-one call (scopes: voice:send) - `GET /api/v1/voice/calls/active`: Read the active call for this key's owner (scopes: voice:read) - `GET /api/v1/voice/calls/{id}`: Read a call session (scopes: voice:read) - `GET /api/v1/voice/power-dialer/sessions`: List power dialer sessions (scopes: voice:read) - `POST /api/v1/voice/power-dialer/sessions`: Open or resume a power dialer session (scopes: voice:draft) - `GET /api/v1/voice/power-dialer/sessions/{sessionId}`: Read a power dialer session (scopes: voice:read) - `POST /api/v1/voice/power-dialer/sessions/{sessionId}/next`: Dial the next power dialer contact (scopes: voice:send) - `GET /api/v1/exports/contacts`: Export contacts (scopes: exports:read) - `GET /api/v1/audit-log`: Read this organization's audit log (scopes: logs:read) - `GET /api/v1/registration/brand`: Read texting brand-registration status (scopes: registration:read) - `POST /api/v1/registration/brand`: File a texting brand registration (scopes: registration:submit) - `POST /api/v1/registration/brand/preview`: Preview texting brand-registration validation (scopes: registration:read) - `GET /api/v1/registration/campaign`: Read texting campaign-registration readiness (scopes: registration:read) - `POST /api/v1/registration/campaign/site-check`: Run the free campaign website preflight (scopes: registration:prepare) - `PUT /api/v1/registration/campaign/draft`: Save a texting campaign compliance draft (scopes: registration:prepare) - `POST /api/v1/registration/campaign/submit`: File the texting campaign registration (scopes: registration:submit) - `GET /api/v1/billing/balance`: Read organization funds (scopes: billing:read) - `GET /api/v1/billing/transactions`: Page the organization ledger (scopes: billing:read) - `GET /api/v1/billing/auto-topup`: Read Auto Top-Up configuration (scopes: billing:read) - `POST /api/v1/billing/auto-topup`: Save Auto Top-Up configuration (scopes: billing:write) - `POST /api/v1/team/invitations/{id}/resend`: Re-queue a pending invitation email (scopes: team:manage) - `POST /api/v1/team/invitations/{id}/cancel`: Cancel a pending invitation (scopes: team:manage) - `PATCH /api/v1/team/members/{id}`: Change a member's role (scopes: team:manage) - `DELETE /api/v1/team/members/{id}`: Remove an ordinary member (scopes: team:manage) - `POST /api/v1/team/members/{id}/suspend`: Suspend an ordinary member (scopes: team:manage) - `POST /api/v1/team/members/{id}/reactivate`: Reactivate a suspended member (scopes: team:manage) ## Workflows ### Billing: inspect funds and transaction history (`billing-read`) Read funds, ledger history and existing Auto Top-Up configuration without initiating a payment or changing settings. Preconditions: - An organization-bound credential with billing:read and the app's owner/admin financial access. Required scopes: billing:read. - `get_billing_balance`: Read available funds and outstanding reservations in whole USD cents. Holds are already deducted from the available balance; do not subtract them again. - Preconditions: billing:read. - Outputs: Available balance, outstanding holds, currency and observation time. These are separate reads, not an atomic snapshot. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_billing_transactions`: Read the requested ledger page. When collecting the full history, advance offset by rows actually returned and continue until hasMore is false; do not mistake a server-capped page for the whole ledger. - Preconditions: billing:read. Use a bounded limit and an offset beginning at zero for a new traversal. - Outputs: Public ledger entries and exact pagination total/hasMore. Entries can change between requests; deduplicate by public entry id during a traversal. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `get_auto_topup_settings`: Inspect current replenishment settings, caps, cooldown and saved-card summary. An absent settings row is not permission to add a card or charge one. - Preconditions: billing:read. - Outputs: Saved configuration and limited payment-method summary, or null when no configuration exists. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Funding and payment-method management: Balance and ledger reads and existing Auto Top-Up configuration are available through billing tools. Adding or removing a payment method, initiating a deposit, and recording an offline payment have no tool. Card entry stays in the secure payment interface, never in a chat or tool argument. ### Billing: configure existing Auto Top-Up (`billing-configure`) Change an explicitly requested replenishment configuration, then read the persisted settings back. This is not a deposit or card-entry workflow. Preconditions: - The user has authorized this configuration change, not merely requested a balance check. - An active organization owner/admin credential with billing:read and billing:write. - Enabling requires an already-saved, already-authorized card and permits future automatic charges. Incomplete setup does not block this configuration control. Required scopes: billing:read, billing:write. - `get_auto_topup_settings`: Read the existing configuration before choosing the complete requested enabled, threshold_cents and amount_cents values. Do not infer an authorization to enable automatic payments from this read. - Preconditions: billing:read. - Outputs: Current settings, saved-card summary and existing guardrails, or null. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `update_auto_topup_settings`: Save the requested enabled flag, trigger balance and replenishment amount. Preserve the existing card, authorization metadata, caps and cooldown. - Preconditions: billing:write. Explicit complete configuration: enabled boolean; threshold_cents 500–100000; amount_cents 1000–100000; amounts are whole cents. An already-authorized saved card when enabling. - Outputs: The saved configuration. Read it back and compare all three requested settings before reporting success. - Side effects: Updates configuration and records a routine audit row. Enabling permits future automatic charges; this call does not collect payment, add a card or deposit funds. - Read back with: `get_auto_topup_settings` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read back after an uncertain result. The same configuration is idempotent, but do not overwrite a newer user change blindly. Bounded by gaps: - Funding and payment-method management: Balance and ledger reads and existing Auto Top-Up configuration are available through billing tools. Adding or removing a payment method, initiating a deposit, and recording an offline payment have no tool. Card entry stays in the secure payment interface, never in a chat or tool argument. ### Connect and discover capabilities (`connect`) Authenticate, then read exactly what this credential may do before doing anything else. Nothing here changes data. Preconditions: - An organization API key (Authorization: Bearer cc_live_...) or an OAuth 2.1 access token issued by this server. - The key's issuer is an active owner or admin of the organization. Required scopes: any valid credential. - `get_capabilities`: Read the granted scopes, the organization the credential acts in, and whether setup is complete. This is the only tool a credential may call before setup is finished. - Preconditions: A valid credential. - Outputs: Granted scopes. Organization binding. Setup completion and the questions to ask the operator. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Credentials and connected apps: Creating, listing, rotating, or revoking API keys and OAuth connected apps has no tool. A key is minted and revoked by a signed-in owner in Account Settings. ### Inspect and repair organization setup (`setup-repair`) Read the sender and organization settings, show the exact missing fields, and save a complete packet so sends are accepted. Preconditions: - Owner or admin role for the save step. - The settings:read scope to read them and settings:write to save. Required scopes: settings:read, settings:write. - `get_organization_settings`: Read the committee identity, mailing address, sending identity, entity metadata, and the exact missing fields. - Preconditions: settings:read. - Outputs: Saved settings. A setup.complete flag and the missing field list. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `get_communications_settings`: Read the sender from name, from address, reply-to, the public compliance block, and the sending domain's authentication state with the records it requires. - Preconditions: settings:read. - Outputs: Sender and compliance summary. The sending domain's state and, for an owned domain, the exact DNS records to publish. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `preview_sending_domain`: Check whether authenticating the From domain would be eligible before making any change. - Preconditions: settings:read. - Outputs: The proposed domain, an eligibility flag, the blockers, and the side effects of configuring it. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `get_sending_domain`: Read the sending domain's authentication state and the exact DNS records that must be published. - Preconditions: settings:read. - Outputs: Ownership, verification state, and the required DNS records. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `configure_sending_domain`: Create, recheck, or resume the sending domain so its DNS records can be published and verified. - Preconditions: settings:write. Owner or admin. Action confirmed with the operator. - Outputs: The updated domain state and the records to publish. - Side effects: Creates or rechecks a domain identity. Does not send, charge, or turn on a channel. - Read back with: `get_sending_domain` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-running ensure or verify is safe: it rechecks the domain rather than creating a duplicate identity. - `update_organization_settings`: Save the whole settings form once the operator has supplied the missing values. - Preconditions: settings:write. Owner or admin. Values confirmed with the operator. - Outputs: The saved settings and the recomputed setup completion. - Side effects: Replaces the organization settings form. Does not send, register, or charge. - Read back with: `get_organization_settings` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-sending the identical form is safe; it overwrites with the same values and does not send. Bounded by gaps: - Sending domain and DNS configuration: Reading an organization's sending-domain authentication state, previewing a configuration, and creating, rechecking, or resuming the domain identity are available through get_sending_domain, preview_sending_domain, and configure_sending_domain and the versioned /api/v1/communications/sending-domain operations. The Agent API returns the exact DNS records an owned domain requires. Publishing those records at the customer's own DNS host stays with the customer, and provider-hosted DNS changes, domain deletion or detach, and custom MAIL FROM are out of scope. Do not present MAIL FROM as configured. - Phone numbers and defaults: Purchasing a toll-free or local number and setting the organization's default campaign phone are available through purchase_phone_number and set_default_phone_number. Local numbers are automatically attached to the organization's current 10DLC campaign when one is approved. ### Personal profile and preferences (`personal-profile`) Read and change the credential owner's OWN personal profile and preferences: name, personal phone, time zone, country, sender defaults, SMS signature, quiet hours, opt-in preferences, and notification preferences. The effect is user-wide: these settings belong to the person across every organization they belong to. Preconditions: - The profile:read scope to read and profile:write to save. - The credential owner is an active owner or admin (current eligibility). Required scopes: profile:read, profile:write. - `get_personal_settings`: Read the owner's own personal profile and preferences, including the normalized phone readback. Returns only the owner's own row, never another user's, and never a role, administrator flag, balance, credential, or secret. - Preconditions: profile:read. - Outputs: The saved personal profile. The normalized personal phone readback. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `update_personal_settings`: Save a personal profile/preference patch once the operator has confirmed the values. Only the fields passed are changed; an omitted field is kept. - Preconditions: profile:write. Owner or admin. Values confirmed with the operator. - Outputs: The saved personal profile and the list of changed fields. - Side effects: Updates the owner's own personal row. A saved phone is normalized but NOT verified, provisioned, or rung. Notification preferences govern future alerts only; saving them does not send, schedule, or charge. - Read back with: `get_personal_settings` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-sending the identical patch is safe; it writes the same values and does not send. Bounded by gaps: - Member and volunteer delegated credentials: Operational portal credentials require an active owner or admin membership in the organization, matching the dashboard's access boundary. Eligible owners and admins can read and update their own personal profile and preferences, which apply across every organization they belong to. Members with calling access can use OAuth calling-workspace tools (calls:read and calls:operate), while broader member or volunteer credential delegation is not available. Do not present general member or volunteer operational access as available. ### Build and confirm an audience (`audience`) Find or create contacts, resolve a natural-language audience to a saved list, size it, and snapshot its membership. Everything here is free and reversible. Preconditions: - contacts:read and lists:read to read; contacts:write and lists:write to change. Required scopes: lists:read, lists:write, contacts:read. - `resolve_list`: Turn a plain-language audience ('my donors') into one saved list, or a set of candidates to confirm. - Preconditions: lists:read. - Outputs: A single resolved list, or needs_confirmation with candidates. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `preview_list`: Count how many contacts a saved-audience definition would match without saving it. - Preconditions: lists:read. - Outputs: A contact count, never the contacts themselves. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `create_segment`: Save the audience as a reusable list once its filters are agreed. - Preconditions: lists:write. The filters confirmed with the operator. - Outputs: The new list id and its matched contact count. - Side effects: Creates one saved list. Sends nothing and charges nothing. - Read back with: `get_segment` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating creates a second list. Check get_segment first if a retry is uncertain. - `get_segment`: Read a saved list by id, including its filters and current contact count. - Preconditions: lists:read. - Outputs: The stored list record. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `change_segment_members`: Add or remove contacts on a static tag list. - Preconditions: lists:write. A static list whose only filter is tags. At most 100 contact ids per call. - Outputs: The number of members added or removed. - Side effects: Adds or removes the list's tags on those contacts. Sends nothing and charges nothing. - Read back with: `get_segment` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Adding an already-present member is a no-op; the call reports the change it made. - `list_contacts`: Search, sort, and filter contacts by name, email, or phone to build or check a list. - Preconditions: contacts:read. - Outputs: A page of contacts, ordered by sort_by (created_at, first_name, last_name, email, phone) and sort_order (asc, desc), optionally kept to those that have an email, phone, or address on file with filter_type. An exact email or phone returns one contact and ignores q, tag, sort_by, sort_order, filter_type, page, and limit. A presence filter means the field is not NULL in the database, not that it is verified, deliverable, or opted in. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Campaign (texting) registration: Reading the stored campaign-registration readiness and running the free website-compliance preflight are available through get_texting_campaign_registration and check_texting_campaign_site. Paid campaign filing is available through submit_texting_campaign_registration (POST /api/v1/registration/campaign/submit) when the credential holds registration:submit and the active organization owner has completed setup. An uncertain carrier outcome is never retried blindly. ### Email: draft, test, send, report (`email`) Build an email piece, verify the audience and cost, send one proof, send or schedule the live campaign, then read recipients and delivery events back. Preconditions: - email:read, email:draft, and email:test for the draft and proof; email:send for the live send and schedule. - Setup complete (sender identity). - An agreed audience, message, and spend. Required scopes: email:read, email:draft, email:test, email:send, logs:read. - `create_email_draft`: Create an unsent email draft with a name, subject, body, and a recipient selection. - Preconditions: email:draft. - Outputs: The draft campaign id. - Side effects: Creates one unsent draft. Sends nothing and charges nothing. - Read back with: `get_email_draft` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating creates a second draft. Read the campaign back first if a retry is uncertain. - `set_email_audience`: Set or replace the draft's audience and rebuild its recipient snapshot. - Preconditions: email:draft. An unscheduled draft. - Outputs: The updated audience and recipient count. - Side effects: Changes the draft's recipient snapshot only. - Read back with: `get_email_draft` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-applying the same audience selection is safe, but a segment is re-resolved at call time: the same saved selection can match a changed contact set, so read the draft back to confirm the current recipient count rather than assuming the previous set. - `preview_email_audience`: Size the would-be campaign and read its estimated cost and blockers before spending. - Preconditions: email:draft. - Outputs: Recipient count. Estimated cost. Blockers. - Side effects: No data change beyond the routine audit row; never charges. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_test_recipients`: See which extra addresses may receive a test send, and whether each one is still pending or already verified. - Preconditions: email:test. - Outputs: Each address and its pending or verified status. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `add_test_recipient`: Allow one more address to receive test sends after that address confirms the verification link. - Preconditions: email:test. - Outputs: The address and whether a verification email was sent. - Side effects: Sends one verification email when the address is new or the previous link is old enough to replace. Does not send a campaign and does not charge. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating a verified address sends nothing. A pending address is not emailed again within ten minutes. - `remove_test_recipient`: Take an address off the test-recipient list. - Preconditions: email:test. - Outputs: Confirmation that the address was removed. - Side effects: The address can no longer receive test sends unless it is the owner or an active member. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Removing an address that is already gone returns not found. Read the list again before retrying. - `test_email_campaign`: Send one proof to the credential owner, an active member, or a verified test recipient. - Preconditions: email:test. Recipients are the key owner's address, active members of the same organization, or verified test recipients. - Outputs: Per-recipient test results. - Side effects: Sends real email to those test addresses and bills each test on its own. - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Every proof is paid: it needs confirm true and a fresh idempotency_key even for a single recipient, because the runner gates the whole tool and not a recipient count. Reuse the same key so a repeat does not send a second proof. - Choice: Deliver now or schedule for later? These options are mutually exclusive; take exactly one per run: - `send_email_campaign`: Send the prepared campaign now and spend organization balance. - Preconditions: email:send. The operator has agreed to the audience, message, and cost. confirm true and a fresh idempotency_key. An unscheduled draft. - Outputs: The accepted send result: the campaign moves to sending/queued. Per-recipient delivery and the final charge are NOT final at this point. - Side effects: Queues real delivery and spends organization balance as sends are accepted, not necessarily in one charge. Delivery cannot be recalled. - Read back with: `get_email_draft` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. The call returns an accepted send, not a finished delivery. Read the campaign status back, then read list_email_events for the queued/sent/delivered evidence before treating it as done; reuse the same idempotency_key so a repeat does not send a second time. - `schedule_email_campaign`: Queue the prepared campaign for a future send at a chosen ISO-8601 time. - Preconditions: email:send. A draft owned by this organization. A time at least 5 minutes ahead and no more than 366 days out. - Outputs: The scheduled campaign with its scheduled_at (queued, not sent). - Side effects: Does not deliver now and places no balance hold on the queue. Two money events differ: the spend approval is consumed now, when the campaign is queued, and the balance hold is placed later, when the send job runs. - Read back with: `get_email_draft` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: A retry of the same schedule with the same idempotency_key returns the scheduled campaign and does not move the time. - Guidance: Take exactly one. `send_email_campaign` starts delivery immediately; `schedule_email_campaign` queues delivery for a future time. They are not sequential steps: an agent must never call both for the same draft in one run. - `list_email_recipients`: Read the per-recipient delivery rows for a campaign. - Preconditions: logs:read. - Outputs: Recipient rows with status and timestamps. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_email_events`: Read delivery events (queued, sent, delivered, bounced, opened, clicked) for a campaign. Use this as the delivery evidence after a send. - Preconditions: logs:read. - Outputs: Delivery events with status. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. ### SMS: readiness, draft, send, report (`sms`) Check texting readiness, draft a text or MMS, preview the audience, send one proof, send or schedule, then read recipients, events, and outcomes. Preconditions: - sms:read, sms:draft, and sms:test for the draft and proof; sms:send for the live send and schedule. - Texting readiness satisfied (brand registered and a sending number assigned). Required scopes: sms:read, sms:draft, sms:test, sms:send, logs:read. - `get_sms_readiness`: Check whether the organization can text: registration state and an assigned sending number. - Preconditions: sms:read. - Outputs: A readiness verdict with the reasons it is not ready. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `create_sms_draft`: Create an unsent text or MMS draft with its message and audience. Set type mms and pass an image to make a picture message; the image is stored as a publicly readable asset. - Preconditions: sms:draft. - Outputs: The draft campaign id. - Side effects: Creates one unsent draft. An MMS image is stored as a publicly readable asset. Sends nothing and charges nothing. - Read back with: `get_sms_campaign` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating creates a second draft. Read it back first if a retry is uncertain. - `set_sms_audience`: Replace an unscheduled text draft's audience and rebuild its recipient snapshot and cost estimate. - Preconditions: sms:draft. An unscheduled draft. - Outputs: The updated audience, recipient count, and cost estimate. - Side effects: Changes the draft's recipient snapshot and estimate only. Sends nothing and does not move organization balance. - Read back with: `get_sms_campaign` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-applying the same audience selection is safe, but a list is re-resolved at call time: the same saved selection can match a changed contact set, so read the draft back to confirm the current recipient count rather than assuming the previous set. - `update_sms_draft`: Revise an unsent draft's name, message, or channel. On an MMS draft a text-only edit keeps the current image, a new image replaces it, and setting type sms removes it. - Preconditions: sms:draft. - Outputs: The updated draft. - Side effects: Updates one unsent draft. A replacement image is stored as a publicly readable asset. Sends nothing and charges nothing. - Read back with: `get_sms_campaign` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating revises the same draft. Read it back first if a retry is uncertain. - `preview_sms_audience`: Size the would-be text and read its estimated cost before spending. - Preconditions: sms:draft. - Outputs: Recipient count and estimated cost. - Side effects: No data change beyond the routine audit row; never charges. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `test_sms_campaign`: Send one proof text to the credential owner or up to five same-organization addresses. - Preconditions: sms:test. Same recipient rules as the email proof. - Outputs: Per-recipient test results. - Side effects: Sends real texts to those test addresses and bills each. - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Every proof is paid: it needs confirm true and a fresh idempotency_key even for a single recipient, because the runner gates the whole tool and not a recipient count. Reuse the same key so a repeat does not send a second proof. - Choice: Deliver now or schedule for later? These options are mutually exclusive; take exactly one per run: - `send_sms_campaign`: Dispatch the prepared text now and spend organization balance. - Preconditions: sms:send. The operator has agreed to the audience, message, and cost. confirm true and a fresh idempotency_key. - Outputs: The accepted dispatch result: the campaign moves to sending/queued. Per-recipient delivery and the final charge are NOT final at this point. - Side effects: Queues real dispatch and spends organization balance as sends are accepted, not necessarily in one charge. Delivery cannot be recalled. - Read back with: `get_sms_campaign` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. The call returns an accepted dispatch, not a finished delivery. Read the campaign status back, then read list_sms_events and get_sms_outcomes for the sent/delivered/failed evidence before treating it as done; reuse the same idempotency_key so a repeat does not send a second time. - `schedule_sms_campaign`: Queue the prepared text for a future dispatch at a chosen ISO-8601 time. - Preconditions: sms:send. A draft owned by this organization. - Outputs: The scheduled campaign with its scheduled_at (queued, not sent). - Side effects: Does not dispatch now and places no balance hold on the queue. Two money events differ: the spend approval is consumed now, when the campaign is queued, and the balance hold is placed later, when the send job runs. - Read back with: `get_sms_campaign` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: A retry of the same schedule with the same idempotency_key returns the scheduled campaign and does not move the time. - Guidance: Take exactly one. `send_sms_campaign` dispatches immediately; `schedule_sms_campaign` queues dispatch for a future time. They are not sequential steps: an agent must never call both for the same draft in one run. - `list_sms_recipients`: Read the per-recipient delivery rows for a text campaign. - Preconditions: sms:read and logs:read. - Outputs: Recipient rows with status. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_sms_events`: Read delivery events for a text campaign. - Preconditions: sms:read and logs:read. - Outputs: Delivery events with status. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `get_sms_outcomes`: Read the delivery outcome summary (delivered, undelivered, failed) for a text campaign. - Preconditions: sms:read and logs:read. - Outputs: Outcome counts. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Campaign (texting) registration: Reading the stored campaign-registration readiness and running the free website-compliance preflight are available through get_texting_campaign_registration and check_texting_campaign_site. Paid campaign filing is available through submit_texting_campaign_registration (POST /api/v1/registration/campaign/submit) when the credential holds registration:submit and the active organization owner has completed setup. An uncertain carrier outcome is never retried blindly. ### Transactional email and templates (`transactional`) Manage reusable transactional templates, then send a one-off system email. Templates are a save, not a send; the send is a confirm-gated paid action. Preconditions: - email:transactional. - A verified sending domain for the From address. Required scopes: email:transactional. - `list_email_templates`: List the organization's saved transactional templates. - Preconditions: email:transactional. - Outputs: Template summaries with ids. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `create_email_template`: Save a new transactional template with a subject and at least one body. - Preconditions: email:transactional. A unique name within the organization. - Outputs: The new template id. - Side effects: Creates one saved template. Sends nothing and charges nothing. - Read back with: `get_email_template` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: A duplicate name is refused with 409; a repeat creates no second row. - `update_email_template`: Change a template's subject or body. Only the fields you pass change. - Preconditions: email:transactional. - Outputs: The updated template. - Side effects: Updates one saved template. Sends nothing and charges nothing. - Read back with: `get_email_template` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating the same values is safe; an empty update is refused with 400. - `send_transactional_email`: Send one transactional email using a template or inline subject and body. - Preconditions: email:transactional. A From address on a verified domain. The recipient confirmed with the operator. confirm true and a fresh idempotency_key. - Outputs: The send result and log id. - Side effects: Sends real email and charges the organization balance. - Read back with: `get_transactional_email` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. If the outcome is uncertain, read the log row back; reuse the same idempotency_key so a repeat is a no-op. - `get_transactional_email`: Read one transactional email's log row by id. - Preconditions: email:transactional. - Outputs: The stored log row. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. ### Texting brand registration: preview, optional approval, submit, status (`brand-registration`) Read the stored brand filing state, preview a would-be packet, optionally route it through the configured approval policy, submit the paid filing, and read the status back. Preconditions: - registration:read to preview and read status; registration:submit to submit. - Owner role and complete setup for submission. - An agreed one-time brand filing fee. Required scopes: registration:read, registration:submit. - `get_texting_brand_registration`: Read the stored brand filing state, the authoritative fee, and whether the credential is the active, fully-set-up owner. - Preconditions: registration:read. - Outputs: Filing state, fee, and eligibility that means role-and-setup only (not spending authority). - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `preview_texting_brand_registration`: Validate would-be brand details against the saved settings and return the required fixes and the fee. - Preconditions: registration:read. - Outputs: A ready flag that means field validation passed only, the missing fields, and the fee. - Side effects: No data change beyond the routine audit row; files nothing and charges nothing. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `request_texting_brand_approval`: When this key has a configured spend limit, request authorization for the filing instead of submitting directly. This is the existing configured policy, not a new consent gate. - Preconditions: registration:submit. The key has a standing spend limit set by its owner. - Outputs: An approval id to pass to the submit step once someone approves. - Side effects: Records an approval request. Files nothing and charges nothing. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: A new request supersedes nothing; read the approval list back if unsure. - `submit_texting_brand_registration`: Submit the paid brand filing built from the saved identity and the credential owner's representative fallbacks. - Preconditions: registration:submit. Owner role and complete setup. A valid preview. confirm true, a fresh idempotency_key, and the fee acknowledgement. - Outputs: The filing result and the charged fee. - Side effects: Charges the non-refundable one-time brand fee and files with the carrier. Cannot be recalled. - Read back with: `get_texting_brand_registration` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. If the carrier outcome is uncertain, read the status back and reconcile; reuse the same idempotency_key so a replay does not double-charge or double-file. ### Texting campaign registration: read readiness and run the free site preflight (`campaign-registration-readiness`) Read the stored campaign-registration state, save a compliance draft, and run the free website-compliance preflight. Paid campaign filing is available through submit_texting_campaign_registration. Preconditions: - registration:read to read readiness; registration:prepare to run the free site preflight. - Owner or admin role: the dashboard's campaign surface is owner/admin only, and the versioned endpoints match that boundary. - An organization-bound credential. Reading readiness works while setup is incomplete; the site preflight requires a complete setup. Required scopes: registration:read, registration:prepare. - `get_texting_campaign_registration`: Read the current filing status, rejection and retry notes, the frozen filed packet, the resumable draft, and the persisted free site-check history. - Preconditions: registration:read. - Outputs: The locally stored filing state and whether the credential can resume Step 2. The frozen filed packet and the current draft (your own brand names, URLs, and copy). The append-only site-check history with timestamps. The filing flags are local only: already_filed means a carrier campaign reference is recorded, not approval or delivery, and a missing reference is not proof the carrier received nothing; confirmation_required or submitting mean do not submit another filing; none of these flags establish whether a fee was charged, so a missing carrier identity is never permission to submit. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `check_texting_campaign_site`: Validate the opt-in, privacy, and terms URLs against the live public pages and return the same carrier checklist the paid submit enforces, recording one timestamped history row. - Preconditions: registration:prepare. Public https opt-in, privacy, and terms URLs on the organization's own domain; a platform or reseller host is refused. A complete organization setup. - Outputs: Whether the site passes, the failing labels, and the per-page checks. One immutable history row with the authenticated organization and the check time. - Side effects: Fetches the organization's public pages and appends one append-only history row. Charges nothing, files nothing with any carrier, and creates no profile or number. - Read back with: `get_texting_campaign_registration` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Not idempotent: each call appends a new timestamped history row. Safe to repeat with the same URLs; a failed append is returned as an error, never as durable evidence. Bounded by gaps: - Campaign (texting) registration: Reading the stored campaign-registration readiness and running the free website-compliance preflight are available through get_texting_campaign_registration and check_texting_campaign_site. Paid campaign filing is available through submit_texting_campaign_registration (POST /api/v1/registration/campaign/submit) when the credential holds registration:submit and the active organization owner has completed setup. An uncertain carrier outcome is never retried blindly. ### Physical mail: draft, preview, launch (`mail`) Create a physical-mail piece with base64 artwork, size the audience, launch the campaign, and read its status. Mail is the one channel that cannot be recalled. Preconditions: - mail:read to preview; mail:draft to create; mail:send to launch. - An agreed audience, artwork, and spend. Required scopes: mail:read, mail:draft, mail:send. - `create_mail_draft`: Create a physical-mail draft, assembling the base64 artwork into the upload the endpoint expects. Confirm-gated. - Preconditions: mail:draft. confirm true and a fresh idempotency_key. - Outputs: The draft campaign id. - Side effects: Creates one draft with its artwork. Prints nothing and charges nothing yet. - Read back with: `get_mail_campaign` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Reuse the same idempotency_key so a retry does not create a second draft. - `preview_mail_audience`: Size the physical-mail audience and read the estimated cost. - Preconditions: mail:read. - Outputs: Recipient count and estimated cost. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `launch_mail_campaign`: Launch the physical-mail campaign and charge the organization balance. - Preconditions: mail:send. The operator has agreed to the audience, artwork, and cost. confirm true and a fresh idempotency_key. - Outputs: The launch result and charged amount. - Side effects: Spends organization balance and hands the pieces to print and mail. Mail cannot be recalled. - Read back with: `get_mail_campaign` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. The retry window for an uncertain piece is the print vendor's 24-hour idempotency key; past it a retry prints a second piece. Stop and ask a person. - `get_mail_campaign`: Read one physical-mail campaign by id, including its status. - Preconditions: mail:read. - Outputs: The stored campaign record. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. ### Voice and the power dialer (`voice`) Prepare voice assets and campaigns, place calls, and run the operator-first power dialer. Readiness (a voice-enabled number and the operator's personal phone) is required before the first call. Preconditions: - voice:read, voice:draft, and voice:send as needed. - Readiness satisfied: a voice-enabled number, a resolvable connection, and the operator's personal phone set in account settings. Required scopes: voice:read, voice:draft, voice:send. - `list_voice_numbers`: List the organization's voice-enabled numbers to confirm one is available. - Preconditions: voice:read. - Outputs: Voice-capable numbers. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `upload_voice_asset`: Upload a base64 voicemail audio asset for a campaign. - Preconditions: voice:draft. Audio no larger than the documented upload limit. - Outputs: The stored asset reference. - Side effects: Stores one audio asset. Places no call and charges nothing. - Read back with: `get_voice_campaign` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Re-uploading the same asset is safe; it stores another copy. - `create_voice_draft`: Create an unsent voice (voicemail) campaign with its audience and asset. - Preconditions: voice:draft. - Outputs: The draft campaign id. - Side effects: Creates one unsent campaign. Places no call and charges nothing. - Read back with: `get_voice_campaign` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Repeating creates a second draft. Read it back first if a retry is uncertain. - `launch_voice_campaign`: Launch the voicemail campaign and charge the organization balance. - Preconditions: voice:send. The operator has agreed to the audience and cost. confirm true and a fresh idempotency_key. - Outputs: The launch result and charged amount. - Side effects: Spends organization balance and starts real voicemail delivery. - Read back with: `get_voice_campaign` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. If the outcome is uncertain, read the campaign back; reuse the same idempotency_key. - `start_voice_call`: Place a single voice call to one contact. - Preconditions: voice:send. The contact confirmed. confirm true and a fresh idempotency_key. - Outputs: The call session id. - Side effects: Places one real call and bills the connected minute. - Read back with: `get_voice_call` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. Read the active call back before placing another. - `start_dialer_session`: Open or resume a power dialer session for a campaign. Calls stay operator-first. - Preconditions: voice:draft. A campaign id. Readiness satisfied. - Outputs: The session id. - Side effects: Dials the operator once and keeps the agent leg up. Bills 6 cents per operator-connected started minute. - Read back with: `get_dialer_session` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Do not retry blindly. Read the session list back to find the existing session. - `advance_dialer_session`: Advance the dialer to the next contact against the live operator leg. - Preconditions: voice:send. A live session. confirm true and a fresh idempotency_key. - Outputs: The next contact, or that the session is finished. - Side effects: Hangs up on the current contact, keeps the operator leg, and dials the next contact. - Read back with: `get_dialer_session` - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly. Read the session back; a repeat can dial a second contact. - `get_active_call`: Read the currently active call for this organization. - Preconditions: voice:read. - Outputs: The active call, or nothing when none is live. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Phone numbers and defaults: Purchasing a toll-free or local number and setting the organization's default campaign phone are available through purchase_phone_number and set_default_phone_number. Local numbers are automatically attached to the organization's current 10DLC campaign when one is approved. ### Import a contact or suppression file (`contact-import`) Upload one or more CSV/XLSX workbooks, preview what will change, answer every uncertain decision, commit, and read the persisted counts and per-row outcomes back. Nothing is imported by parsing a file alone; the commit is the mutation. Preconditions: - contacts:read to preview and read back; contacts:write to commit and to stage a large upload. - Optional list cleaning needs email:verify and address:verify, but is NOT performed by the import: it runs as a separate cohort-scoped step (clean_import_cohort) keyed by the cohortId the commit returns, and the organization-wide verification tools are never run off a file's permission. Required scopes: contacts:read, contacts:write, email:verify, address:verify. - `stage_contact_import_upload`: Send a contact file that is too large to inline in one call, in bounded chunks. The first chunk omits staging_id and returns one; every later chunk sends that id, and final true marks the last chunk. The staging id is the reference a later import commit uses for the file. - Preconditions: contacts:write. The file bytes as base64, up to 10 MB decoded per chunk. - Outputs: A staging id, the 0-based chunk index just written, the bytes received so far and whether the upload is complete. - Side effects: Stores the chunk bytes for this credential's organization. Changes no contact, parses nothing and charges nothing. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Safe to read back with the returned staging id; re-sending an entire chunk would append it a second time, so reconcile by the returned chunk index before repeating any call. - `preview_contact_import`: Parse and classify the uploaded files and return the exact counts, unresolved decisions, suppression matches and a signed previewToken. Send the per-tab plan, list tags and reviewChoices to keep the operator's decisions. - Preconditions: contacts:read. The CSV/XLSX files as base64; no file path or URL is accepted. - Outputs: A classified preview, the row counts, unresolved decision ids, and a previewToken. - Side effects: Writes preview evidence for this credential's organization. Changes no contacts. Charges nothing. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Safe to call again; each preview supersedes the last. Read the counts back before committing. - `commit_contact_import`: Commit the previewed import with the previewToken and the same files and options. Resolve every uncertain decision first. - Preconditions: contacts:write. A fresh previewToken. No unresolved decisions. email:verify / address:verify when cleaning is requested. - Outputs: The persisted counts, the job id, and the cohortId when the commit was preview-backed. When cleaning was requested, a `cleaning` block reporting the requested channels as requested and NOT performed by the commit. - Side effects: Creates, merges and suppresses contacts and writes the import job and per-row evidence. It NEVER charges and NEVER cleans inline: no list cleaning runs, and no organization-wide verification is triggered. - Read back with: `get_contact_import_outcomes` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Do not retry blindly. Re-sending the unchanged commit with the same previewToken replays the stored receipt without importing twice; a changed file or mapping needs a new preview. Cleaning runs only through clean_import_cohort on the returned cohortId; never run a whole-organization verification on the strength of this file's permission. - `list_contact_imports`: List past imports for this organization, newest first, to find a job to inspect or confirm one landed. - Preconditions: contacts:read. - Outputs: A bounded page of imports with counts and an honest continuation. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `get_contact_import_outcomes`: Read the persisted per-row outcomes of one import, optionally filtered to one kind, with a keyset continuation. - Preconditions: contacts:read. A job id from commit_contact_import or list_contact_imports. - Outputs: A bounded page of outcome rows with a next_cursor and has_more. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again; advance by the returned cursor, never assume a short page is the end. - `get_import_cleaning_cohort`: Read back the frozen import identity cohort captured with a preview-backed commit, using the cohortId the commit returned. - Preconditions: contacts:read. A cohortId returned by commit_contact_import (preview-backed commits only). - Outputs: The cohort's safe metadata: preview and job references, snapshot version/fingerprint, requested cleaning channels, the CAPTURED UNIQUE identity counts, a capture state and whether cohort-scoped paid cleaning is available. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again; a foreign or absent cohort is 404. It reports capture and availability only; it never reports or runs any cleaning. - `clean_import_cohort`: Clean exactly one frozen import identity cohort and charge organization balance. It acts only on the contacts matching the cohort's captured email keys and complete-address hashes, never on the whole organization and never on a caller-supplied key list. - Preconditions: email:verify and address:verify. A cohortId returned by a preview-backed commit_contact_import. confirm true and a unique idempotency_key. - Outputs: Per requested channel: whether cleaning was requested and performed, the counts, and the approved maximum. Nothing is charged when no cohort identity needs cleaning. - Side effects: Spends organization balance and updates the matched contacts' verification verdicts. It cannot be undone. - Confirmation: required. The runner refuses this call without confirm true and a well-formed idempotency_key. - Retry: Do not retry blindly after an uncertain provider result: read the cohort back and the contacts' verification state first. A retry that finds no identity needing cleaning charges nothing and returns the unused approval. Bounded by gaps: - Import list cleaning: A commit never cleans and never charges. Cleaning exactly the contacts a single import saved is a SEPARATE, bounded paid step: clean_import_cohort acts on the cohortId a preview-backed commit returns and on nothing else, so a file's cleaning permission never triggers a whole-organization run. A commit that asks for cleaning reports the channels as REQUESTED and NOT PERFORMED by the commit. An import that is not preview-backed captures no cohort, so there is nothing for the cohort-scoped step to act on. - Large import files and staged uploads: An MCP contact import travels base64-encoded inside one JSON-RPC body, and the request body cap is 4.5 MB, so the inline aggregate is bound near 2.6 MiB decoded. A larger file can be sent in bounded chunks with stage_contact_import_upload, which stores the bytes and returns a durable staging id. Pass that staging_id to preview_contact_import or commit_contact_import in a JSON body to import the staged file. The dashboard multipart import and the multipart REST route remain available for callers that prefer them. ### Website form intake: record a submission and read the receipt back (`form-intake`) Record a website form submission as a contact through the same consent and suppression engine the website endpoint uses, then read the persisted receipt back by the external id. Nothing is sent and nothing is charged. Preconditions: - An organization-bound credential with intake:write and the app's owner/admin operational access. - An active organization. Intake is a contact write that works before organization onboarding is complete, matching the legacy website endpoint. Required scopes: intake:write. - `record_form_submission`: Create or update one contact from a form submission. Provide an email, a phone, or both, and a source; a checked opt-in box needs its verbatim consent text, and an unchecked box withholds that channel. - Preconditions: intake:write. An email, a phone, or both, and a source. - Outputs: The contact id, the created/updated/duplicate result, the applied list tag, and each channel verdict. An identity_conflict refusal naming both contacts when the email and the phone already belong to two different contacts. - Side effects: Creates or updates one contact and records one routine audit row. Applies source-derived list tags and channel consent. Sends nothing and charges nothing. A dated opt-out, a STOP, or a live suppression is never cleared. - Read back with: `get_form_submission` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Reuse the same external_id on a retry so a COMPLETED submission replays the stored receipt as a duplicate rather than a second write. The adapter commits the contact and its receipt in one serialized transaction, so a same-key concurrent duplicate yields a single mutation and the first winner's receipt, and no receipt is written without its contact. A stored receipt records the outcome of the first accepted write, not proof the stored contact still matches this request, because later legitimate edits can change the contact. An interrupted (transport-ambiguous) keyed retry is not auto-resent: read the stored receipt for this external_id first. With no external_id there is no dedupe or replay; a repeated unkeyed submission is a separate operation. - `get_form_submission`: Read the persisted receipt of a past submission by the external id that was sent, to confirm the outcome or recover after an interrupted attempt. - Preconditions: intake:write. The external_id sent with the original submission. - Outputs: The stored created/updated/duplicate receipt, or the stored identity_conflict receipt naming both unchanged contacts. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. The receipt is the historical outcome of the first accepted write. A keyed submission writes its receipt with its contact in the same transaction, so an interrupted keyed attempt is resolved by reading this receipt: a present receipt proves the commit landed, and its absence after a failed request means no mutation was committed. The receipt does not show whether a later legitimate edit changed the contact, so look the contact up directly when that matters. Bounded by gaps: - Form-intake write atomicity: A website form submission commits its contact mutation and its stored receipt in a single serialized transaction per organization. It checks for an existing same-key receipt before mutating, re-validates the prepared contact and email/phone matches at commit time, and writes the receipt in the same transaction. A same-key concurrent duplicate commits a single mutation and replays the first winner's receipt, and a receipt failure rolls the contact write back. A stored receipt records the outcome of the first accepted write, not proof that the current contact still matches that request, as later edits can update the contact. After an interrupted keyed request, read the stored receipt for that external_id before retrying. Submissions without an external_id perform the contact action without deduplication or replay. ### Team: read the roster and current pending invitations (`team-roster`) Read the authorized organization's team roster and its CURRENT pending invitations without making changes. To invite, re-queue, or cancel an invitation, or to manage an ordinary member, use the team-management workflow. Preconditions: - An organization-bound credential with team:read whose issuing owner/admin is an ACTIVE member of the organization. - A read-only organization state (for example a locked organization) stays readable; a suspended or removed credential membership does not. Required scopes: team:read. - `list_team_members`: Read a bounded page of the roster: id, email, display name, canonical role and membership status, membership timestamps, and the protected-owner flag. Advance by the returned page while hasMore is true; total is the true number of members. - Preconditions: team:read. Owner or admin role on an active membership. - Outputs: A page of members with an honest pagination block. Each member's role/status come from THIS organization's membership, so a member whose own selected organization is elsewhere is still shown with this organization's role. - Side effects: No business-state change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_team_invitations`: Read a bounded page of the CURRENT pending invitations (not accepted, not cancelled, not dismissed, not expired), newest first: id, email, role, created_at, expires_at. - Preconditions: team:read. Owner or admin role on an active membership. - Outputs: A page of pending invitations with an honest pagination block. No invitation token or copy-link value, and no history of past or accepted invitations. - Side effects: No business-state change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. Bounded by gaps: - Member and team management: An authorized owner/admin credential can read the organization's roster and its CURRENT pending invitations through list_team_members and list_team_invitations (scope team:read, owner/admin only), and can invite, re-queue, and cancel a team invitation and change, suspend, reactivate, or remove an ordinary (non-owner) member through a narrow set of team:manage tools and /api/v1/team operations. The management surface is ordinary organization authority only: the organization owner cannot be acted on, self-actions are refused, promotion to owner is refused, the last active admin cannot be suspended or removed, and platform is_admin alone is not authority. An invitation write queues a source email event, which is NOT proof of delivery. Two gaps remain: an invitee's own identity-plane actions (accepting, previewing, or dismissing their own invitation) and the credential lifecycle (creating, listing, rotating, or revoking API keys and OAuth connected apps) still have no tool and stay in the signed-in product. Do not present invitee-side acceptance or credential management as a runnable step. ### Contact fields: discover the registry and pin a field's type (`contact-fields`) Read the organization's contact-field registry and, when a field should be typed, save an explicit key/label/type definition and read it back. A definition save changes field metadata only; it does not convert, clear, or repair any stored value. Preconditions: - An organization-bound credential with contacts:read to list fields and contacts:write to save a definition. - The organization is the one that owns the credential; no argument selects it. Required scopes: contacts:read, contacts:write. - `list_contact_fields`: Read the registry: the explicit definitions unioned with the types inferred from stored contact values, keyed by their exact field keys. - Preconditions: contacts:read. - Outputs: The full bounded registry with a truthful total. Keys are verbatim, including spaces and case; each field carries key, label, and type. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `upsert_contact_field`: Save an explicit key/label/type definition for one field so it can be filtered as that type. - Preconditions: contacts:write. An agreed field key, label, and supported type (text, number, date, boolean). - Outputs: The stored definition read back in field. confirmed is false when the save committed but the stored row could not be read back; that is not a refusal. - Side effects: Writes one field-definition row and records a routine audit row. It does NOT convert, clear, or repair any contact's stored value and does not change a stored value's type. - Read back with: `list_contact_fields` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: A repeated identical save settles the same key/label/type and is safe; do not replay an unknown (unconfirmed transport) outcome blindly; read the registry back first. Bounded by gaps: - Contact-field value typing and advanced filter parity: An agent can DISCOVER the contact-field registry (list_contact_fields) and SAVE an explicit key/label/type definition (upsert_contact_field), and the versioned route GET|PUT /api/v1/contacts/fields backs both. But saving a definition changes field METADATA only: it does NOT convert, clear, or repair any contact's stored value, and it does not change a stored value's type; a value that no longer matches the new type keeps its stored text. Writing a TYPED field VALUE onto a contact, and the advanced typed-filter surface (typed operators such as gt/gte/lt/lte/neq/contains over typed values), still have no tool and no Agent API operation. Only the flat text/number custom-value write on create_contact/update_contact is available. Do not present a definition save as a value conversion, and do not present registry discovery or a definition save as advanced filter parity. ### Team: read the roster and manage invitations and ordinary members (`team-management`) Read the authorized organization's roster and current pending invitations, then invite, re-queue, or cancel an invitation, or change, suspend, reactivate, or remove an ordinary (non-owner) member, with each management action read back from the live state. This is ordinary owner/admin authority inside the credential's organization; the organization owner is never a target. Preconditions: - An organization-bound credential holding BOTH team:read and team:manage, whose issuing owner/admin is an ACTIVE member of the organization. - Confirm each management action with the operator before it runs; none is money-moving or irreversible, so no confirm/idempotency argument is required. Required scopes: team:read, team:manage. - `list_team_members`: Read the current roster (id, email, display name, canonical role/status, protected-owner flag) before changing anything. - Preconditions: team:read. - Outputs: A bounded page of members with an honest pagination block; total is the true member count. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `list_team_invitations`: Read the CURRENT pending invitations (id, email, role, created_at, expires_at) to find the one to re-queue or cancel. - Preconditions: team:read. - Outputs: A bounded page of pending invitations; no invite token or copy-link value is ever returned. - Side effects: No data change beyond the routine audit row. - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read-only. Safe to call again. - `create_team_invitation`: Invite one person by email as admin, member, or volunteer. Only email and role are accepted; a person who already has a membership must be reactivated instead. - Preconditions: team:manage. An agreed email and role. - Outputs: Safe invitation metadata and a `queued` acknowledgment: a source email event was written, which is NOT proof the email was sent or delivered. - Side effects: Creates one pending invitation and queues a source email event. Creates no organization, grants no role, and moves no money. - Read back with: `list_team_invitations` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read the pending invitations back after an uncertain result. A repeated invite for the same live pending invitation returns a conflict rather than a second one. - `resend_team_invitation`: Re-queue the invitation email for a still-pending invitation. - Preconditions: team:manage. A still-pending invitation id. - Outputs: The re-queue acknowledgment; the email itself is not sent by this call. - Side effects: Queues another source email event for the same pending invitation. Sends no email itself and moves no money. - Read back with: `list_team_invitations` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: This is NOT idempotent: there is no request-key store, so each call queues the email again as a new request. Read the invitation state back instead of retrying blindly. - `cancel_team_invitation`: Cancel a still-pending invitation so the person can be invited again. - Preconditions: team:manage. A still-pending invitation id. - Outputs: The cancellation result; the pending invitation is no longer listed. - Side effects: Marks the invitation cancelled. Sends nothing and moves no money. - Read back with: `list_team_invitations` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Cancelling an already accepted, cancelled, or dismissed invitation is refused. Read the invitations back before re-inviting. - `change_team_member_role`: Change an ordinary member's role between admin and member. - Preconditions: team:manage. A non-owner member id and the new role. - Outputs: The live membership read back (id, email, role, membership_status); an unconfirmable readback is reported uncertain, not as success. - Side effects: Changes one ordinary member's role. Promotion to owner, changing the owner, and changing your own role are refused. Sends nothing and moves no money. - Read back with: `list_team_members` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read the roster back after an uncertain result before repeating; a repeat settles the same role. - `suspend_team_member`: Suspend an ordinary active member, dropping them to read-only. - Preconditions: team:manage. A non-owner active member id. - Outputs: The live membership read back with its membership_status. - Side effects: Suspends one ordinary membership. The owner cannot be suspended, you cannot suspend yourself, and the last active admin cannot be suspended. Sends nothing and moves no money. - Read back with: `list_team_members` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read the roster back; a repeat against an already-suspended member is refused. - `reactivate_team_member`: Reactivate a suspended member. - Preconditions: team:manage. A suspended, non-owner member id. - Outputs: The live membership read back with its restored membership_status. - Side effects: Reactivates one suspended membership. The owner cannot be acted on and you cannot reactivate yourself. Sends nothing and moves no money. - Read back with: `list_team_members` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Read the roster back; a member who is not suspended is refused, so a repeat is safe and reports the refusal. - `remove_team_member`: Remove an ordinary member, ending their membership. This cannot be undone through this surface. - Preconditions: team:manage. A non-owner member id, confirmed with the operator. - Outputs: The removal result; removed members are no longer listed. - Side effects: Ends one ordinary membership. It does not delete the account, transfer data, or move money. The owner cannot be removed, you cannot remove yourself, and the last active admin cannot be removed. - Read back with: `list_team_members` - Confirmation: no additional confirm/idempotency_key gate is declared by this tool. This does not establish whether it writes data, reserves funds, or enables future charges; follow its documented side effects. - Retry: Do not treat removal as reversible here. Read the roster back to confirm the outcome; re-inviting the person starts a fresh invitation. Bounded by gaps: - Member and team management: An authorized owner/admin credential can read the organization's roster and its CURRENT pending invitations through list_team_members and list_team_invitations (scope team:read, owner/admin only), and can invite, re-queue, and cancel a team invitation and change, suspend, reactivate, or remove an ordinary (non-owner) member through a narrow set of team:manage tools and /api/v1/team operations. The management surface is ordinary organization authority only: the organization owner cannot be acted on, self-actions are refused, promotion to owner is refused, the last active admin cannot be suspended or removed, and platform is_admin alone is not authority. An invitation write queues a source email event, which is NOT proof of delivery. Two gaps remain: an invitee's own identity-plane actions (accepting, previewing, or dismissing their own invitation) and the credential lifecycle (creating, listing, rotating, or revoking API keys and OAuth connected apps) still have no tool and stay in the signed-in product. Do not present invitee-side acceptance or credential management as a runnable step. ## Scopes - `email:read`: Read email campaigns and drafts - `email:draft`: Create and edit email drafts and their audiences - `email:test`: Send one email test - `email:send`: Send and schedule live email - `email:transactional`: Send one-off transactional email and manage its templates - `agent:runs`: Create and update this credential's work runs - `lists:read`: Read, preview, and resolve saved audiences - `lists:write`: Create, edit, and change membership of saved lists - `contacts:read`: Read and search contacts, and read the contact-field registry - `contacts:write`: Create and update contacts, tags, channel consent, and contact-field definitions - `sms:read`: Read text campaigns, recipients, and outcomes - `sms:draft`: Create and edit text drafts and preview audiences - `sms:test`: Send one text test - `sms:send`: Send and schedule live text - `voice:read`: Read voice campaigns, calls, numbers, and dialer sessions - `voice:draft`: Create voice drafts, upload assets, and open dialer sessions - `voice:send`: Place calls and advance dialer sessions - `calls:read`: List the prepared calling queues you may work, and your own active or paused shift - `calls:operate`: Save your own operator phone in the member calling workspace - `mail:read`: Read physical-mail campaigns and preview audiences - `mail:draft`: Create physical-mail drafts - `mail:send`: Launch a physical-mail campaign - `email:verify`: Quote and run email verification - `address:verify`: Quote and run postal address verification - `settings:read`: Read sender and organization settings - `settings:write`: Save organization settings - `profile:read`: Read your own personal profile and preferences - `profile:write`: Save your own personal profile and preferences - `suppression:read`: Read do-not-contact records - `suppression:write`: Record or override do-not-contact records - `logs:read`: Read delivery events and the audit log - `conversions:write`: Record a conversion - `intake:write`: Accept a website form submission as a contact - `exports:read`: Export contacts - `inbox:read`: Read inbox conversations for reply triage - `registration:read`: Read texting brand registration status and preview a would-be filing - `registration:prepare`: Read campaign-registration readiness and run the free campaign website-compliance preflight - `registration:submit`: Request approval for and submit a paid texting brand registration - `billing:read`: Read organization funds, ledger transactions and saved Auto Top-Up settings - `billing:write`: Configure Auto Top-Up against an already-authorized saved card; enabling permits future automatic charges - `team:read`: Read the organization team roster and its current pending invitations - `team:manage`: Invite, re-queue, and cancel team invitations, and change, suspend, reactivate, or remove an ordinary (non-owner) team member Scope failures: - A call the credential is not allowed to make returns a tool error naming missingScopes and does not run the action. - Each tool publishes the exact scopes it needs in its metadata, so an agent can discover the complete required scope set for an action before requesting it. Those declared scopes are the complete set for the action, not a per-permission menu, and the manifest's tools[].scopes and scope inventory describe SUPPORTED scopes, not granted scopes. - For an eligible active, full-access OAuth actor missing an in-role scope, the server returns the standard HTTP 403 insufficient-scope challenge, and the tool result may carry an optional matching hint that a capable host can use to begin a NEW, explicit, user-approved consent for the required permissions. The optional hint supplements and never replaces the standard signal. Interactive reauthorization requires host support; if the host does not start it automatically, use its connection settings to request new, explicit, user-approved consent. - For an API-key credential, the fix for a missing scope is an intentional credential update by an authorized owner or admin, who mints a new key with that scope and stores it only in the host's secret settings, a vault, or the environment, never in chat, a URL, or source. An existing grant never widens silently, and the same call is never retried hoping it passes. - Calls outside the role ceiling, or without eligible active/full actor state, remain plain refusals rather than an invitation to reconnect. Do not present a refusal as a fabricated consent, a permission prompt, or proof that an account is disconnected. - Adding an email, text, mail, or voice send scope is an explicit grant; older keys keep only the scopes they originally had. - An empty scope list on a tool (or an operation with no x-required-scopes) means any valid credential, not a public endpoint. A public operation is one whose route security is empty: that is a property of the route that answers without a credential, and it is separate from whether a tool needs a scope. - A scope declared by a tool but absent from this document is a documentation defect, not a scope an agent may invent; treat unknown scopes as a stop, never as a silent no-op. ## Retry and idempotency - Never retry a paid or irreversible action blindly. If the outcome is uncertain, read the resource back before acting again. - Tools marked requiresConfirmation require confirm true and an idempotency_key. Generate a new key for a new operation; preserve that key and payload for retries of the same operation. Follow the tool's documented replay window and uncertain-outcome rules; a reused key is not an unlimited guarantee against another effect. - Do not assume an action finished asynchronously. Read the stored status back; there is no invented background completion. ## Approvals - There is no blanket consent screen and no agent-only approval gate. Authentication and granted scopes authorize actions directly. - A key whose owner configured a standing spend limit may route a paid action through the existing approval policy; without a configured limit the action runs directly under its scope. ## Known capability gaps - Campaign (texting) registration (`gap.campaign-registration`): Reading the stored campaign-registration readiness and running the free website-compliance preflight are available through get_texting_campaign_registration and check_texting_campaign_site. Paid campaign filing is available through submit_texting_campaign_registration (POST /api/v1/registration/campaign/submit) when the credential holds registration:submit and the active organization owner has completed setup. An uncertain carrier outcome is never retried blindly. - Member and team management (`gap.member-management`): An authorized owner/admin credential can read the organization's roster and its CURRENT pending invitations through list_team_members and list_team_invitations (scope team:read, owner/admin only), and can invite, re-queue, and cancel a team invitation and change, suspend, reactivate, or remove an ordinary (non-owner) member through a narrow set of team:manage tools and /api/v1/team operations. The management surface is ordinary organization authority only: the organization owner cannot be acted on, self-actions are refused, promotion to owner is refused, the last active admin cannot be suspended or removed, and platform is_admin alone is not authority. An invitation write queues a source email event, which is NOT proof of delivery. Two gaps remain: an invitee's own identity-plane actions (accepting, previewing, or dismissing their own invitation) and the credential lifecycle (creating, listing, rotating, or revoking API keys and OAuth connected apps) still have no tool and stay in the signed-in product. Do not present invitee-side acceptance or credential management as a runnable step. - Credentials and connected apps (`gap.team-credentials`): Creating, listing, rotating, or revoking API keys and OAuth connected apps has no tool. A key is minted and revoked by a signed-in owner in Account Settings. - Sending domain and DNS configuration (`gap.domain-configuration`): Reading an organization's sending-domain authentication state, previewing a configuration, and creating, rechecking, or resuming the domain identity are available through get_sending_domain, preview_sending_domain, and configure_sending_domain and the versioned /api/v1/communications/sending-domain operations. The Agent API returns the exact DNS records an owned domain requires. Publishing those records at the customer's own DNS host stays with the customer, and provider-hosted DNS changes, domain deletion or detach, and custom MAIL FROM are out of scope. Do not present MAIL FROM as configured. - Funding and payment-method management (`gap.billing-and-autotopup`): Balance and ledger reads and existing Auto Top-Up configuration are available through billing tools. Adding or removing a payment method, initiating a deposit, and recording an offline payment have no tool. Card entry stays in the secure payment interface, never in a chat or tool argument. - Calling execution, media, and reach (`gap.calling-execution`): A member's connected agent can read the prepared calling queues it may work and save the member's own operator phone (calls:read and calls:operate). Placing, advancing, media, or billing a call, buying or provisioning a number, and the full personal-settings, key-management, and account portals remain unavailable to a member credential and stay in the signed-in product. Do not present starting, advancing, or billing a call as a runnable step for a member connection. - Phone numbers and defaults (`gap.phone-defaults`): Purchasing a toll-free or local number and setting the organization's default campaign phone are available through purchase_phone_number and set_default_phone_number. Local numbers are automatically attached to the organization's current 10DLC campaign when one is approved. - Import list cleaning (`gap.import-list-cleaning`): A commit never cleans and never charges. Cleaning exactly the contacts a single import saved is a SEPARATE, bounded paid step: clean_import_cohort acts on the cohortId a preview-backed commit returns and on nothing else, so a file's cleaning permission never triggers a whole-organization run. A commit that asks for cleaning reports the channels as REQUESTED and NOT PERFORMED by the commit. An import that is not preview-backed captures no cohort, so there is nothing for the cohort-scoped step to act on. - Large import files and staged uploads (`gap.import-large-files`): An MCP contact import travels base64-encoded inside one JSON-RPC body, and the request body cap is 4.5 MB, so the inline aggregate is bound near 2.6 MiB decoded. A larger file can be sent in bounded chunks with stage_contact_import_upload, which stores the bytes and returns a durable staging id. Pass that staging_id to preview_contact_import or commit_contact_import in a JSON body to import the staged file. The dashboard multipart import and the multipart REST route remain available for callers that prefer them. - Form-intake write atomicity (`gap.form-intake-atomicity`): A website form submission commits its contact mutation and its stored receipt in a single serialized transaction per organization. It checks for an existing same-key receipt before mutating, re-validates the prepared contact and email/phone matches at commit time, and writes the receipt in the same transaction. A same-key concurrent duplicate commits a single mutation and replays the first winner's receipt, and a receipt failure rolls the contact write back. A stored receipt records the outcome of the first accepted write, not proof that the current contact still matches that request, as later edits can update the contact. After an interrupted keyed request, read the stored receipt for that external_id before retrying. Submissions without an external_id perform the contact action without deduplication or replay. - Member and volunteer delegated credentials (`gap.member-credential-parity`): Operational portal credentials require an active owner or admin membership in the organization, matching the dashboard's access boundary. Eligible owners and admins can read and update their own personal profile and preferences, which apply across every organization they belong to. Members with calling access can use OAuth calling-workspace tools (calls:read and calls:operate), while broader member or volunteer credential delegation is not available. Do not present general member or volunteer operational access as available. - Contact-field value typing and advanced filter parity (`gap.contact-field-typed-values`): An agent can DISCOVER the contact-field registry (list_contact_fields) and SAVE an explicit key/label/type definition (upsert_contact_field), and the versioned route GET|PUT /api/v1/contacts/fields backs both. But saving a definition changes field METADATA only: it does NOT convert, clear, or repair any contact's stored value, and it does not change a stored value's type; a value that no longer matches the new type keeps its stored text. Writing a TYPED field VALUE onto a contact, and the advanced typed-filter surface (typed operators such as gt/gte/lt/lte/neq/contains over typed values), still have no tool and no Agent API operation. Only the flat text/number custom-value write on create_contact/update_contact is available. Do not present a definition save as a value conversion, and do not present registry discovery or a definition save as advanced filter parity. ## Requirements and availability - Connect through a host that supports remote MCP and OAuth 2.1. Sign in, choose your organization, and approve the requested scopes. After initialization and tool discovery, call get_capabilities to confirm your organization, granted scopes, and setup state. Configure the host connection once; tool access depends on your current role and granted scopes. - The self-service MCP connection uses OAuth authorization-code flow with PKCE S256 and public-client registration (token_endpoint_auth_method=none). No manually supplied client secret or manually minted API key is required. Use the host's connection settings to complete sign-in and consent. - Business access requires an organization-bound credential and eligible membership. Follow each tool's role, scope, setup, billing, and confirmation requirements; connecting does not grant additional permissions or complete business setup. - An authorized owner/admin credential can read the organization roster and current pending invitations and manage ordinary (non-owner) team members. An invitee's own identity-plane actions and credential lifecycle actions (creating, listing, rotating, or revoking API keys and OAuth connected apps) have no MCP tool. Billing balance, ledger and existing Auto Top-Up configuration tools are available. Consult each capability gap for its remaining limitation. - A registered texting brand alone is not SMS campaign approval or full texting readiness. Reading the stored campaign-registration readiness and running the free website-compliance preflight are available through get_texting_campaign_registration and check_texting_campaign_site; paid campaign filing is available through submit_texting_campaign_registration when the credential holds registration:submit. Check get_sms_readiness before treating a brand as ready to text. - For hosts that support API-key authentication, configure an existing valid Campaign Commander API key only in the host's secret settings, vault or environment. Hosts that require OAuth should use the MCP OAuth connection flow; no API-key entry field is assumed. Never paste a credential into chat or a tool argument, and do not treat an unauthenticated 401 as proof that an account is disconnected. - A campaign registration's already_filed flag means a carrier campaign reference is recorded, not approval, delivery, or a charge, and a missing reference does not prove the carrier received nothing. confirmation_required or submitting mean an existing attempt must not be filed again; reconcile it with support. None of these flags establish whether a review fee was charged, so a missing carrier identity is never permission to submit. - Each tool publishes its complete required scope set. An eligible active/full-access OAuth actor missing an in-role scope receives the standard HTTP 403 insufficient-scope challenge and may receive an optional matching tool-result hint. The host must support interactive OAuth reauthorization to request new, explicit, user-approved consent; use its connection settings if it does not start that flow automatically. Security policy is published in _meta.securitySchemes, not a top-level securitySchemes field; hosts that require the top-level field must support this metadata location to use it.