Model Context Protocol · Agency plan

RingTonic MCP server

Let an AI agent — Claude, Grok, Cursor, Codex, and more — operate your RingTonic account in plain language: campaigns, call tracking, contacts, call flows, appointments, and analytics, over the same services the dashboard and REST API use. 51 tools, one endpoint.

Endpoint (Streamable HTTP)
POST https://ringtonic.app/mcp

Available only on ringtonic.app. MCP is RingTonic-branded, so it never runs on a white-label agency domain.

Quick start

  1. 1
    Create a key. A workspace owner or admin on the Agency plan mints one at Settings → MCP. Pick only the scopes the agent needs. The rt_ key is shown once — copy it.
  2. 2
    Connect your client. Add the endpoint above with your key as a bearer token (per-client setup below).
  3. 3
    Ask. Try “Which client accounts can I manage with this connection?” then work in plain language — the agent picks the tools.

Authentication

Every request carries a bearer token — a RingTonic personal access token minted for MCP:

Authorization: Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

MCP keys are user-bound, not workspace-bound: one key reaches every workspace where its creator is an owner or admin, narrowed by the scopes you grant. Reach is checked on every call — remove or demote the member and the key loses that workspace immediately. A key always carries at least one resource scope; a REST API key or an all-abilities key is rejected and never reaches the server.

Choosing a workspace

Every tool that touches account data takes a workspace — a workspace public uuid (stable and canonical) or an exact name (a convenience; names are not unique and can be renamed, so prefer the uuid). When your key reaches exactly one workspace the parameter is optional and defaults to it. When unsure, ask “which accounts can I manage?” first — that lists each workspace's uuid, your role, its timezone and currency, and whether its plan enables the API. An unreachable workspace or a foreign uuid answers not-found — the same as one that does not exist, so nothing leaks across accounts.

Scopes

A key grants only what you select. A call to a tool the key lacks answers insufficient-scope, and the tool never appears in the agent's tool list. Grant narrowly — a read-only key yields a read-only agent.

ScopeGrants
campaigns:read / campaigns:write List and read campaigns; create, update, delete them (and attach a call flow).
call-logs:read / call-logs:write List and read calls incl. signed recording/voicemail URLs; update lead status, deal value, tags; delete.
contacts:read / contacts:write List, read, and match contacts; upsert and delete.
conversions:read / conversions:write List conversion events; record a conversion that advances a funnel stage.
tracking-numbers:read / tracking-numbers:write List and read tracking numbers; update friendly name, assignment, and call-control flags.
numbers:provision Search available numbers, purchase, and release. Never bundled into tracking-numbers:write — it spends money.
call-flows:read / call-flows:write List and read flows, versions, and graphs; create, draft, publish, roll back, clone, delete.
appointments:read / appointments:write List and read appointments; book, reschedule, and set status (cancel/complete/no-show).
form-submissions:read List and read captured web-form submissions. Read-only — there is no write scope.
tags:read / tags:write List contact tags; create, update, delete.
products:read / products:write List the product/service catalog; create, update, delete.
blocked-numbers:read / blocked-numbers:write Read the effective block list; block and unblock workspace-scope numbers.
webhooks:read / webhooks:write List and read webhook endpoints + delivery stats; create, update, delete, test, regenerate the signing secret.
analytics:read The four analytics tools — call metrics, attribution, funnel, and per-number performance.
crm:force A modifier only: allows a backward or terminal funnel-stage move (force_stage) inside upsert_contact / record_conversion. Grants no tool of its own and still requires an owner/admin actor.

Conventions

Result shapes
Every result is a JSON object returned as text, matching the field set the REST API uses for that surface — the REST reference is the field-level source of truth. Some list and search tools return compact rows — a scannable subset with null fields omitted — while others, and every get_ detail tool, return the full resource with its fields preserved. search_form_submissions also folds a single-uuid full-detail lookup. List results are wrapped in a cursor envelope (data, next_cursor, prev_cursor), and a few detail tools embed a nested envelope (a contact's events, a call flow's versions). The analytics tools return computed reports, not resource rows.
Pagination
Search and list tools are cursor-paginated: pass per_page (default 25, max 100) and cursor; each result carries next_cursor/prev_cursor. Follow the cursors — never guess offsets. list_workspaces and search_available_numbers return their whole set (no cursor).
Timestamps & timezones
All result times are UTC ISO-8601. A date-only filter (YYYY-MM-DD) is midnight-to-midnight in the target workspace's timezone; a datetime with an offset is taken literally. The four analytics tools take date-only values and echo the effective (possibly clamped) range.
Idempotency
Tools that create or spend accept an optional idempotency_key. Generate one key per operation and reuse the same key on retry — the retry replays the recorded result instead of acting twice. This matters most for purchase_number, book_appointment, and regenerate_secret.
Rate limits
120 tool calls per minute per key; plus a shared per-workspace budget of 300 reads and 60 writes per minute (the same counter the REST API uses). Over a limit answers rate-limit-exceeded with a retry_after. Number purchases also share one daily cap across the API and MCP.
Errors
A failed tool returns a compact JSON error the agent can act on: validation-error, forbidden, insufficient-scope, plan-required, not-found, rate-limit-exceeded, idempotency-conflict, conflict, provision-cap-exceeded, and server-error.

Connect your client

RingTonic authenticates with a bearer API key, so any client that sends an Authorization header works — including the ChatGPT desktop app (via its shared Codex config). Only ChatGPT on the web (chatgpt.com connectors) still needs OAuth and cannot connect yet. Use one of these:

Claude (claude.ai & Claude Desktop)

  1. Open Settings → Connectors → Add custom connector.
  2. Name it RingTonic and set the URL to https://ringtonic.app/mcp.
  3. Leave Authentication as "None", then add a request header — name it exactly Authorization, and paste the value below (the value field only, no header name). Save, then enable RingTonic in the composer.
  4. Request-header authentication is a beta feature and may not be available on every Claude organization or plan yet. If you do not see the option, use Claude Code, Cursor, or Windsurf below.
Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Claude Code (CLI)

  1. Add the server with your key as a bearer header:
claude mcp add --transport http ringtonic https://ringtonic.app/mcp \
  --header "Authorization: Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

ChatGPT Desktop

  1. Codex is now part of the ChatGPT desktop app. Open Settings → MCP servers → Add server, choose Streamable HTTP, and enter a name plus the URL above.
  2. The Streamable HTTP form takes no header, so add the key to the shared config ~/.codex/config.toml (below), then restart ChatGPT.
[mcp_servers.ringtonic]
url = "https://ringtonic.app/mcp"
http_headers = { Authorization = "Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }

Codex CLI

  1. The Codex terminal agent shares the same config. Export the token, then add the server:
export RINGTONIC_MCP_TOKEN=rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
codex mcp add ringtonic --url https://ringtonic.app/mcp \
  --bearer-token-env-var RINGTONIC_MCP_TOKEN

Grok & Grok Bot (grok.com)

  1. In Grok, open grok.com/connectors → New Connector → Custom.
  2. Set the URL to https://ringtonic.app/mcp, and in the connector's authentication field enter the Authorization header below. Grok Bot uses the same connector.
Authorization: Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Grok Build (CLI)

  1. xAI's terminal coding agent connects to remote HTTP servers via grok mcp add / ~/.grok/config.toml. Add https://ringtonic.app/mcp with an Authorization: Bearer rt_… header — see xAI's Grok Build MCP docs for the exact config syntax.

Cursor

  1. Add RingTonic to ~/.cursor/mcp.json (or Settings → MCP → Add):
{
  "mcpServers": {
    "ringtonic": {
      "url": "https://ringtonic.app/mcp",
      "headers": { "Authorization": "Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
    }
  }
}

Windsurf

  1. Add RingTonic to your Windsurf MCP config (~/.codeium/windsurf/mcp_config.json):
{
  "mcpServers": {
    "ringtonic": {
      "serverUrl": "https://ringtonic.app/mcp",
      "headers": { "Authorization": "Bearer rt_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
    }
  }
}

Report recipes for agencies

Paste one of these as a single instruction — the agent chains the tools and reports back. Great for the recurring numbers you send clients.

📞

Show me last week's top campaigns by calls for Downtown Dental.

list_workspaces → get_call_metrics → search_campaigns

🎯

Which tracking numbers are converting best for Acme this month? Rank them by booked appointments.

get_number_performance → search_appointments

🗓️

For the lead who called from (415) 555-0133 yesterday, book a follow-up Thursday 2pm and tag them 'hot-lead'.

search_calls → match_contact → book_appointment → update_call

☎️

Grab me a new local number in area code 512 and wire it to the Spring Promo campaign.

search_available_numbers → purchase_number

💰

Build a Q3 client report for Downtown Dental: total calls, qualified-lead rate, closed revenue, and top 3 sources.

get_call_metrics + get_funnel_metrics + get_attribution_report

🔁

Find every 'won' deal last month and confirm each contact record has a deal value set.

search_conversions → get_contact → upsert_contact

🚦

Audit the Acme account: which campaigns have no call flow attached and which numbers are unassigned?

search_campaigns → get_campaign → search_tracking_numbers

📋

Pull this week's web form submissions on Spring Promo, flag the spam, and show which became contacts.

search_form_submissions → match_contact → get_contact

🧹

For the campaign we're sunsetting, mark yesterday's no-shows, then delete it and return its numbers to inventory.

search_appointments → set_appointment_status → delete_campaign

📈

Across all my client workspaces, tell me which one had the biggest week-over-week jump in qualified calls.

list_workspaces → get_attribution_report (per workspace)

🔒

Block the harassing caller at +1 800 555 0111 and delete the calls they left.

search_calls → manage_blocked_numbers → delete_call

🛠️

Clone the Main IVR, add an after-hours voicemail branch to the copy, and publish it.

clone_call_flow → get_call_flow → update_call_flow_draft → publish_call_flow

Tool catalog

All 51 tools, grouped by area. Each shows its scope, whether it only reads (read-only) or changes data (destructive), its parameters, and a prompt you can copy and try.

Workspaces & meta

list_workspaces any key read-only

List every workspace this key can reach, with the caller's role and whether the workspace's plan currently enables the API/MCP surface. Call this first when unsure which workspace to pass to other tools. The uuid is the stable, canonical identifier to use.

Which client accounts can I manage with this connection?

get_workspace any key read-only

Read one workspace's profile — name, timezone, currency, consent defaults, and the custom-field schema you need before writing custom_fields on contacts.

workspace:string

What timezone and currency is the Downtown Dental account set to, and what custom contact fields does it have?

Campaigns

search_campaigns campaigns:read read-only

List a workspace's campaigns as compact rows (uuid, name, tracking type, routing status, attached call flow). Cursor-paginated. Use get_campaign for a campaign's full detail.

workspace:string tracking_type:string name:string sort:string per_page:integer cursor:string

Show me all the website-tracker campaigns in the Acme account.

get_campaign campaigns:read read-only

Read one campaign's full detail by uuid — tracking config, routing flags, timezone override, and the attached call flow.

workspace:string campaign:string*

Pull up the full configuration for the Spring Promo campaign.

create_campaign campaigns:write write

Create a campaign. tracking_type is "static" (forward a number) or "dni" (website/dynamic-number tracker). Optionally link existing unassigned tracking numbers by uuid. Pass idempotency_key and reuse it on retry so a retry does not create a second campaign.

workspace:string tracking_type:string* name:string* forward_to:string call_recording_enabled:boolean whisper_enabled:boolean spam_filter_enabled:boolean allowed_domains:array phone_tracking_enabled:boolean css_selector:string session_duration:integer swap_target:string voicemail_enabled:boolean tracking_number_uuids:array idempotency_key:string

Set up a new static campaign called 'Radio Spot – Q3' that forwards calls to +1 512 555 0180.

update_campaign campaigns:write write

Update one campaign by uuid (partial — omit a field to leave it unchanged). Also attaches or detaches its call flow: pass call_flow as a flow uuid to attach, or null to detach. Pass idempotency_key and reuse it on retry.

workspace:string campaign:string* name:string forward_to:string|null call_recording_enabled:boolean whisper_enabled:boolean spam_filter_enabled:boolean allowed_domains:array|null phone_tracking_enabled:boolean css_selector:string|null session_duration:integer|null call_screening_enabled:boolean voicemail_enabled:boolean timezone:string|null swap_target:string|null call_flow:string|null idempotency_key:string

Turn on call recording for the Spring Promo campaign and attach my 'Main IVR' call flow to it.

delete_campaign campaigns:write destructive

Delete one campaign by uuid. Requires number_action: "unassign" returns its tracking numbers to your inventory, "release" permanently gives them back to the provider (which additionally needs the numbers:provision scope).

workspace:string campaign:string* number_action:string*

Delete the old Radio Spot campaign and put its tracking numbers back into my inventory.

Call logs

search_calls call-logs:read read-only

List a workspace's calls as compact rows (uuid, direction, status, timing, caller, lead status, campaign, tracking number, contact). Cursor-paginated. Use get_call for a call's full detail, transcript, and recording.

workspace:string campaign:string tracking_number:string contact:string tag:string direction:string status:string lead_status:string caller:string duration_min:integer duration_max:integer started_between:string has_recording:boolean has_voicemail:boolean sort:string per_page:integer cursor:string

List every missed call on the Downtown Dental campaign in the last 7 days.

get_call call-logs:read read-only

Read one call's full detail by uuid — timing, caller, lead status, keyword spots, attribution, and signed recording/voicemail URLs. Pass include_transcript to also return the transcript.

workspace:string call:string* include_transcript:boolean

Give me the recording and full transcript of that call from (415) 555-0133 yesterday.

update_call call-logs:write write

Update one call by uuid: its lead_status (qualification), deal_value (minor units of the workspace currency, e.g. cents), and tags (replaces the whole set — each entry is an existing tag uuid or exact name; [] or null clears them). Omit a field to leave it unchanged.

workspace:string call:string* lead_status:string deal_value:integer|null tags:array|null

Mark that call as a qualified lead worth $2,500 and tag it 'hot-lead'.

delete_call call-logs:write destructive

Permanently delete one call log by uuid. This cannot be undone: the recording, transcript, and daily metrics are removed and the contact's call counts are corrected.

workspace:string call:string*

Permanently delete that spam test call from the log.

Contacts

search_contacts contacts:read read-only

List a workspace's contacts as compact rows (uuid, name, phone, email, company, lead status, source). Cursor-paginated. Use match_contact to resolve identifiers, or get_contact for full detail.

workspace:string lead_status:string stage:string tag:string email:string phone:string external_id:string created_between:string updated_between:string sort:string per_page:integer cursor:string

Show me all contacts at the 'proposal sent' stage for Acme.

match_contact contacts:read read-only

Diagnose which contact a set of identifiers (external_id, phone, email) resolves to, without writing anything. Reports matched / ambiguous / conflicting / no-match with public uuids, so you can pick the right target before an upsert.

workspace:string external_id:string phone:string email:string

Do I already have a contact for the number +1 415 555 0133?

get_contact contacts:read read-only

Read one contact's full detail by uuid — identifiers, stage, deal value, custom fields, tags. Pass include_events to also embed the conversion-event timeline (cursor-paginated under events).

workspace:string contact:string* include_events:boolean events_cursor:string events_per_page:integer

Pull John Carter's full contact record and his conversion history.

upsert_contact contacts:write write

Create or update a contact. Without contact: identify-or-create by external_id/phone/email (a match on phone/email returns the contact unchanged; use contact to update it). With contact (a uuid from match_contact/search_contacts): update that contact — the identifiers become new values, and lead_status/deal_value/force_stage are accepted only here. Send idempotency_key and reuse it on retry.

workspace:string contact:string|null external_id:string|null phone:string|null email:string|null name:string|null company:string|null custom_fields:object|null allow_unknown_fields:boolean lead_status:string deal_value:integer|null force_stage:boolean idempotency_key:string|null

Add a new contact named Maria Gomez, phone +1 305 555 0190, email maria@example.com.

delete_contact contacts:write destructive

Delete one contact by uuid. This is a soft delete: the contact stops appearing in searches, its tags are detached, but its call history is preserved and a later postback with the same identifier restores it.

workspace:string contact:string*

Remove Maria Gomez from the contacts list.

Conversions

search_conversions conversions:read read-only

List a workspace's conversion events (stage change, source, value, linked contact/call/form) as full rows. Cursor-paginated.

workspace:string stage:string source:string contact:string value_min:integer value_max:integer occurred_between:string sort:string per_page:integer cursor:string

List all deals we closed (won conversions) last month with their values.

record_conversion conversions:write write

Record a conversion that moves a contact to a funnel stage and writes an auditable event. Identify the contact by contact uuid or by external_id/phone/email; create_if_missing creates one when nothing matches. won/proposal_sent stages need value_cents (or inherit_value). Send idempotency_key and reuse it on retry.

workspace:string contact:string|null external_id:string|null phone:string|null email:string|null stage:string* value_cents:integer|null inherit_value:boolean create_if_missing:boolean occurred_at:string|null custom_fields:object|null allow_unknown_fields:boolean external_event_id:string|null force_stage:boolean idempotency_key:string|null

Log a won deal worth $4,000 for the contact at +1 305 555 0190.

Tracking numbers & provisioning

search_tracking_numbers tracking-numbers:read read-only

List a workspace's tracking numbers (phone number, friendly name, assigned campaign, call-control flags). Cursor-paginated.

workspace:string campaign:string status:string phone_number:string calls_enabled:boolean sort:string per_page:integer cursor:string

Which of my tracking numbers aren't assigned to any campaign right now?

get_tracking_number tracking-numbers:read read-only

Read one tracking number's full detail by uuid — phone number, friendly name, assigned campaign, provider, CNAM, call-control flags, and ad location.

workspace:string tracking_number:string*

Show me the full details of the (415) 555-0148 tracking number.

update_tracking_number tracking-numbers:write write

Update one tracking number by uuid: friendly_name, campaign_uuid (assign, or null to unassign — re-points the number at the provider), cnam_lookup_enabled, calls_enabled + calls_disabled_action/message (Agency plan), and ad_location. Omit a field to leave it unchanged.

workspace:string tracking_number:string* friendly_name:string|null campaign_uuid:string|null cnam_lookup_enabled:boolean calls_enabled:boolean calls_disabled_action:string|null calls_disabled_message:string|null ad_location:object|null

Rename the 415 number to 'Billboard – I-280' and point it at the Spring Promo campaign.

search_available_numbers numbers:provision read-only

Search the telephony provider for numbers you could buy in a country. Provider-agnostic. Not paginated (one bounded page); narrow with number_type, area_code, contains, or capabilities. Nothing is reserved — a listed number can be sold before you buy it, so on a purchase failure, search again.

workspace:string country:string* number_type:string area_code:string|null contains:string|null capabilities:array

Find me a local number available in the 512 area code.

purchase_number numbers:provision destructive

Buy a tracking number. THIS SPENDS MONEY — always send idempotency_key and reuse it on retry, or a timeout-retry buys a second number. Name an exact phone_number from search_available_numbers, OR give search criteria (country, number_type, area_code, contains, capabilities) to auto-pick the first match — not both. Optionally assign a campaign_uuid and a friendly_name. Subject to the workspace daily purchase cap (shared with the REST API).

workspace:string phone_number:string campaign_uuid:string|null friendly_name:string|null country:string number_type:string area_code:string contains:string capabilities:array idempotency_key:string|null

Buy me a new local tracking number in area code 415 and label it 'Downtown billboard'.

release_number numbers:provision destructive

Release one tracking number by uuid back to the telephony provider. NOT REVERSIBLE: the number leaves your account, stops taking calls immediately, and can be bought by someone else. Call history is kept (soft delete). To take a number out of service without giving it up, unassign it instead (update_tracking_number with campaign_uuid: null).

workspace:string tracking_number:string*

Give up the old 305 number we no longer advertise.

Call flows (IVR)

list_call_flows call-flows:read read-only

List a workspace's call flows (name, description, publish status, live version, attached-campaign count). Cursor-paginated. Use get_call_flow for a flow's versions and graph.

workspace:string name:string status:string campaign:string sort:string per_page:integer cursor:string

List all the call flows in the Acme account and whether they are published.

get_call_flow call-flows:read read-only

Read one call flow's full detail by uuid — status, live version, attached campaigns, and (cursor-paginated under versions) its publish history. Pass include_graph to also return the draft and/or published node/edge graph.

workspace:string call_flow:string* include_graph:string versions_cursor:string versions_per_page:integer

Show me the current draft graph of the Main IVR flow and its version history.

create_call_flow call-flows:write write

Create a call flow. It starts as a draft (no live version) and answers no calls until you publish it. graph is optional — omit it for a blank flow, or send a node/edge graph (Go-To targets are call-flow uuids). A graph is validated at publish strictness now, so an invalid one is rejected at create time. Pass idempotency_key and reuse it on retry so a retry does not create a second flow.

workspace:string name:string* description:string|null graph:object idempotency_key:string|null

Create a new empty call flow called 'After-Hours Routing'.

update_call_flow_draft call-flows:write write idempotent

Replace a call flow's DRAFT graph wholesale (the published version and live routing are untouched until you publish). Read the current graph with get_call_flow (include_graph: draft) first, then send the full nodes/edges graph back. Go-To targets are call-flow uuids. To keep a webhook credential, echo it back as its masked value ******** — it is restored only while the webhook's url and method are unchanged. The graph is validated at publish strictness: invalid graphs return per-node validation errors. A concurrent change to the draft returns a conflict — re-read and re-apply.

workspace:string call_flow:string* nodes:array* edges:array* global_error_node_id:string|null metadata:object|null

Update the After-Hours flow's draft so unanswered calls go to voicemail.

publish_call_flow call-flows:write write

Publish a call flow's draft as a new live version. THIS CHANGES CALL ROUTING: every campaign using this flow starts answering with the new version. A flow with generated audio returns status "publishing" and becomes active (or failed) shortly — poll get_call_flow. A busy flow (a concurrent or still-generating publish) returns a conflict; a draft that cannot go live (invalid graph, a node type your plan lacks, or a required test call) returns a validation-error. Pass idempotency_key and reuse it on retry so a retry does not publish twice.

workspace:string call_flow:string* publish_note:string|null idempotency_key:string|null

Publish the After-Hours Routing flow so it goes live.

rollback_call_flow call-flows:write write idempotent

Restore a past version's graph into the call flow's DRAFT (by version_number, from get_call_flow's versions history). This activates nothing — call publish_call_flow afterwards to put the restored graph live. Pass idempotency_key and reuse it on retry.

workspace:string call_flow:string* version_number:integer* idempotency_key:string|null

Roll the Main IVR back to version 3 as a draft so I can review it before republishing.

clone_call_flow call-flows:write write

Duplicate a call flow by uuid. The copy is a new draft (name suffixed " (Copy)") with no published version — it answers no calls until you publish it. Pass idempotency_key and reuse it on retry so a retry does not create a second copy.

workspace:string call_flow:string* idempotency_key:string|null

Make a copy of the Main IVR flow so I can build a variant.

delete_call_flow call-flows:write destructive

Delete one call flow by uuid. THIS CHANGES CALL ROUTING: every campaign using this flow is detached (call_flow_id nulled) and falls back to its non-flow routing, and its tracking numbers are re-synced at the provider. The flow is soft-deleted. There is no undo through this API.

workspace:string call_flow:string*

Delete the old 'Test IVR' call flow.

Appointments

search_appointments appointments:read read-only

List a workspace's appointments (status, start/end time, title, linked contact and call). Cursor-paginated.

workspace:string status:string campaign:string contact:string start_between:string sort:string per_page:integer cursor:string

Show me all the appointments booked for next week.

get_appointment appointments:read read-only

Read one appointment's full detail by uuid — status, timing, title, notes, location, calendar event id, linked contact and call, and cancellation info.

workspace:string appointment:string*

Pull up the details of Maria Gomez's consultation appointment.

book_appointment appointments:write write

Book an appointment. Identify the customer with contact (a uuid from match_contact/search_contacts) OR an inline new_contact (a phone is required; needs the contacts:write scope). start_at is an ISO-8601 instant WITH an offset or Z; duration_minutes is 5-480. Confirmations and reminders are opt-in. A slot overlap returns a conflict — to book anyway resend with ignore_conflicts under a NEW idempotency_key. Send idempotency_key and reuse it on retry.

workspace:string contact:string|null new_contact:object|null call_log:string|null start_at:string* duration_minutes:integer* title:string|null notes:string|null location:string|null calendar_id:string|null send_sms_confirmation:boolean send_email_confirmation:boolean reminders:array|null ignore_conflicts:boolean idempotency_key:string|null

Book a 30-minute follow-up call with the contact at +1 305 555 0190 for Thursday at 2pm and text them a confirmation.

reschedule_appointment appointments:write write

Reschedule or edit a booked appointment (identified by its uuid). Send only the fields to change: start_at and/or duration_minutes to move it, or title/notes/location to edit details — at least one is required. Only booked appointments can be edited. A slot overlap returns a conflict; resend with ignore_conflicts to override. Not idempotent — a repeat re-syncs the calendar and re-notifies, so do not blind-retry.

workspace:string appointment:string* start_at:string duration_minutes:integer title:string|null notes:string|null location:string|null send_sms_confirmation:boolean send_email_confirmation:boolean ignore_conflicts:boolean

Move Maria's Thursday appointment to Friday at 10am.

set_appointment_status appointments:write destructive idempotent

Set an appointment's status by uuid: cancelled (removes the calendar event, optionally notifies the customer via notify_customer, records reason), completed, no_show, or booked (re-open a terminal appointment). reason, notify_customer and idempotency_key apply only when cancelling; send idempotency_key on a cancellation and reuse it on retry so the retry replays instead of cancelling twice.

workspace:string appointment:string* status:string* reason:string|null notify_customer:boolean idempotency_key:string|null

Mark yesterday's 3pm appointment as a no-show.

Form submissions

search_form_submissions form-submissions:read read-only

List a workspace's captured form submissions (campaign, fields, marketing params, consent, spam flag). Cursor-paginated. Pass uuid to return one submission's full payload instead (cannot be combined with filters, sort, cursor, or per_page).

workspace:string uuid:string campaign:string contact:string is_spam:boolean submitted_between:string sort:string per_page:integer cursor:string

Show me the web form submissions we captured on the Spring Promo campaign this week.

Tags

list_tags tags:read read-only

List a workspace's contact tags (name, color). Cursor-paginated, alphabetical by default.

workspace:string name:string sort:string per_page:integer cursor:string

What contact tags exist in the Acme account?

manage_tags tags:write destructive

Create, update, or delete a workspace contact tag. action: "create" (name required, optional hex color; pass idempotency_key and reuse it on retry so a retry does not create a second tag), "update" (tag uuid + the fields to change), or "delete" (tag uuid). Tag names are unique per workspace.

workspace:string action:string* tag:string name:string color:string|null idempotency_key:string|null

Create a new contact tag called 'VIP' in blue.

Products

list_products products:read read-only

List a workspace's product/service catalog (name, description, category, price range, active flag). Cursor-paginated, alphabetical by default.

workspace:string name:string category:string is_active:boolean sort:string per_page:integer cursor:string

List the services in Acme's catalog and their prices.

manage_products products:write destructive

Create, update, or delete a product/service in the workspace catalog. action: "create" (name required; optional description, category, price_min/price_max in integer cents, is_active; pass idempotency_key and reuse it on retry so a retry does not create a second entry), "update" (product uuid + the fields to change; omit a field to leave it unchanged, send null to clear a nullable one), or "delete" (product uuid).

workspace:string action:string* product:string name:string description:string|null category:string|null price_min:integer|null price_max:integer|null is_active:boolean idempotency_key:string|null

Add a service called 'Teeth Whitening' priced between $150 and $300.

Blocked numbers

list_blocked_numbers blocked-numbers:read read-only

List a workspace's effective blocked numbers, including account-inherited entries (flagged scope "account", read_only true). Cursor-paginated.

workspace:string phone:string reason:string source:string scope:string sort:string per_page:integer cursor:string

Show me the numbers we've blocked on this account.

manage_blocked_numbers blocked-numbers:write destructive

Block or unblock a phone number for the workspace. action: "block" (phone_number + reason; optional note; pass idempotency_key and reuse it on retry) or "unblock" (blocked_number uuid). Only the workspace's own entries can be unblocked — account-inherited entries (scope "account") are read-only.

workspace:string action:string* blocked_number:string phone_number:string reason:string note:string|null idempotency_key:string|null

Block the spam caller at +1 800 555 0111 as harassment.

Webhooks

list_webhook_endpoints webhooks:read read-only

List a workspace's webhook endpoints (url, subscribed events, active flag, health). Cursor-paginated. Use get_webhook_endpoint for a single endpoint's delivery statistics.

workspace:string sort:string per_page:integer cursor:string

What webhook endpoints are configured for Acme and are they healthy?

get_webhook_endpoint webhooks:read read-only

Read one webhook endpoint's full detail by uuid — url, subscribed events, health, and 24-hour delivery statistics (count, success rate, average response time).

workspace:string webhook_endpoint:string*

Show me the delivery stats for our Zapier webhook over the last day.

manage_webhook_endpoints webhooks:write destructive

Manage webhook endpoints. action: "create" (name, https url, events[]; returns the signing secret ONCE — it enters this conversation; pass idempotency_key and reuse it on retry), "update" (webhook_endpoint uuid + fields; setting is_active true re-enables and resets the failure circuit breaker), "delete" (webhook_endpoint uuid), "test" (webhook_endpoint uuid + event_type the endpoint subscribes to — queues a sample delivery; idempotency_key replays instead of sending twice), or "regenerate_secret" (webhook_endpoint uuid — rotates and returns a NEW secret ONCE; ALWAYS pass idempotency_key and reuse it on retry, or a blind retry rotates twice and strands the first value).

workspace:string action:string* webhook_endpoint:string name:string url:string events:array is_active:boolean event_type:string idempotency_key:string|null

Create a webhook to https://hooks.example.com/rt that fires on the appointment.booked event.

Analytics

get_call_metrics analytics:read read-only

Call totals, a day/week/month time series, and status/direction/lead_status breakdowns over a date range, optionally for one campaign. Totals and the time series are inbound marketing analytics (matching the dashboard); the breakdowns count every call in the window across both directions. Dates are YYYY-MM-DD in the workspace (or scoped campaign) timezone.

workspace:string primary_start:string primary_end:string campaign:string group_by:string chart_metric:string

How many calls did each week bring in over the last quarter, and what's the answered-vs-missed split?

get_attribution_report analytics:read read-only

Attribution totals and top source, per-source performance rows (calls, qualified, conversion rate, cost, revenue, ROI), and a per-source-and-medium breakdown over a date range, optionally for one campaign. Dates are YYYY-MM-DD in the workspace timezone.

workspace:string primary_start:string primary_end:string campaign:string

Which marketing sources drove the most qualified calls and best ROI last month?

get_funnel_metrics analytics:read read-only

The lifecycle stage funnel (per-stage counts, exits, and percent-from-previous) plus per-source-and-medium revenue rows (leads, qualified, won, revenue) over a date range, optionally for one campaign. Dates are YYYY-MM-DD in the workspace timezone.

workspace:string primary_start:string primary_end:string campaign:string

Show me the lead funnel for Acme this month — how many leads made it to 'won'?

get_number_performance analytics:read read-only

Per-tracking-number call volume, answered/missed/failed/spam counts, missed rate, and average duration over a date range, optionally for one campaign. Dates are YYYY-MM-DD in the workspace timezone.

workspace:string primary_start:string primary_end:string campaign:string

How many calls did each tracking number get last week, and which had the worst missed-call rate?

A note on safety

  • The agent acts with your key's scopes. Grant the minimum. A key can do anything its scopes allow, with no per-action confirmation from RingTonic — destructive-tool prompts are your client's job, not the server's.
  • Call transcripts, contact fields, and form text are third-party content. Treat them as data an agent reads, never as instructions to follow.
  • Some results are secrets. A regenerated webhook secret and signed media URLs enter the conversation by design; signed URLs expire in one hour.
  • Reach follows the person. Removing or demoting a member cuts their key's access to that workspace on the next call.
Building a server-to-server integration instead? See the REST API reference.