Skip to main content
Zelto exposes a Model Context Protocol server at https://api.zelto.ai/mcp. Any MCP-compatible client can connect and get read access to your agents, conversations, transcripts, reviews, and findings, plus a small set of write tools for the bucket-based review workflow. Most read and write tools mirror a public REST API endpoint, so anything you can do over HTTP you can do from inside your editor. Some tools go beyond the REST surface, though — review queues, changes, bucket population, voice-provider connect/import, and organization settings tools are MCP-only. The Mirrors column in each table below tells you which is which (— (MCP-only) means there’s no REST equivalent).
New here? Onboard with an agent drives the whole setup — connect a provider, get calls in, create a monitor, and route findings — over the onboarding tools below.

Get an API key

Mint one at https://dashboard.zelto.ai/org/<your-slug>/settings/integrations → API Call Upload. The key is shown once — copy it immediately. The same key works for the REST API and every MCP client below. Minting and revoking keys is an owner/admin action; if you hold the member role, ask an owner or admin for one (and see Your role decides what you can do). Export it for the snippets below:

Sign in with OAuth

Clients that support remote MCP OAuth — Claude, Cursor, VS Code, and others — can connect without minting a key. Point the client at the server URL and sign in through your browser:
On first connect the client opens a browser window to sign in to Zelto and authorize access. If you belong to more than one organization you choose which one the connection is scoped to on the consent screen — the client only ever sees that organization’s data, exactly like an org-scoped API key. Access tokens are short-lived (one hour), refresh only while your Zelto session is valid, and stop working as soon as you lose access to that organization. Under the hood the server is a standard OAuth 2.1 Authorization Server as the MCP spec defines it — it publishes Protected Resource Metadata, supports Dynamic Client Registration, and requires PKCE — so a compliant client discovers everything it needs from the URL alone. API keys keep working in parallel; use whichever your client supports.

Clients

Claude Code

Add the server to ~/.claude.json (create the file if it doesn’t exist):
Restart Claude Code (or run /mcp to refresh). The zelto: tools appear in the picker; zelto:health returns your org id as a smoke test.
The integrations page also shows this snippet pre-filled with the current host — go to Settings → Integrations → AI agent access and open the Claude card.

OpenAI Codex CLI

Codex CLI supports MCP servers via ~/.codex/config.toml. Add a server entry under [mcp_servers.zelto]:
Then run codex and ask it something that needs your data, e.g. “list my last 20 conversations and group failures by agent.” Codex discovers the zelto: tools automatically.

Cursor

~/.cursor/mcp.json accepts the same shape as Claude Code:
Then open Cursor’s MCP panel and toggle the zelto server on. The tools show up in the chat sidebar.

Continue, Zed, and other Streamable-HTTP clients

The server speaks Streamable HTTP, so any client that accepts a URL + Authorization header works. Continue uses config.yaml:
Zed uses ~/.config/zed/settings.json under context_servers.zelto with the same url + headers shape.

Quick smoke test (curl)

If a client is misbehaving, verify your key and connectivity against the REST API — the same key authenticates both:
A 200 with a { "data": [...] } page means the server and your key are good — the problem is in the client config. From inside a connected client, the health tool returns { "status": "ok", "orgId": "…" } as the same check. A 401 means the credential is the problem: the key was deleted, or the person it belongs to lost access to the organization (removed, or their account banned). Minting a fresh key only helps if your own account still has access to that org.

Authentication

Every request carries an Authorization: Bearer <token> header, where the token is either an API key or an OAuth access token obtained by signing in. Requests with no token or an invalid/expired token return 401 and never reach a tool. Either way the token resolves to a single organization server-side — an API key from its baked-in org, an OAuth token from the org you chose at consent — and a tool only ever sees that organization’s data.

Your role decides what you can do

Authenticating proves which organization you may act on, not what you may do inside it. Tools that administer the organization itself require the owner or admin role — the same gate the dashboard applies: update_org_settings · invite_member · remove_member · save_usage_report_config · set_voice_tool_access · delete_agent · set_findings_delivery · merge_findings · delete_finding · approve_finding_candidate · reject_finding_candidate · update_finding (only when you pass title — the triage fields stay open to any member) update_member_role is stricter still: owner only, and it refuses to change your own role. remove_member likewise refuses to remove your own membership, whatever your role — ask another owner or admin. A token belonging to a member reaches every read tool and every non-administrative write, but the tools above return Only an organization owner or admin can perform this action. instead of acting (update_member_role returns Only an organization owner can perform this action.). This is true for both credential types: a key or token acts as the person behind it, and this gate reads that person’s role at call time rather than at mint time. To take a credential out of service entirely, revoke it — see API keys. Removing that person from the organization (or banning their account) does the same thing implicitly: every request re-checks the owner, so their keys and OAuth tokens stop working on the next call.

Read and write scopes

An OAuth client asks for an access scope at consent time, and the server enforces it before a tool runs:
  • write — every tool, including the mutating ones. Implies read.
  • read — the read tools only. A read-scoped token that calls a mutating tool gets a tool error instead of a result: This credential is read-only and cannot call "<tool>". Reconnect with the "write" scope to allow changes. The call never reaches the tool, and the attempt is recorded in the organization’s MCP activity.
  • No access scope — tokens minted before scopes existed keep full access.
API keys carry full access; a key is not narrowed by scope. Whichever credential you use, the role gate still applies on top. Send one JSON-RPC message per POST /mcp. A JSON-RPC batch array that contains a tool call is refused with 400; request bodies over 4 MiB are refused with 413.

Tools

Read tools

read_docs is MCP-only — call it with no arguments to list every documentation page (slug + title + description), then pass a path (e.g. findings or integrations/slack) to read that page’s full Markdown. status enums: reviews accept pending / reviewed / flagged; findings accept open / acknowledged / resolved / ignored. Finding priority accepts none / low / medium / high / urgent.

Write tools

annotate_finding_conversation (add a system-authored, optionally audio-ranged annotation to a call within a finding) requires a platform-admin API key and the X-Organization-Id header — it is not callable with a standard org key. Regular org keys can write the same annotation over REST at POST /v1/findings/[id]/conversations/[conversationId]/comments (attributed to the calling user), or read them via list_finding_conversation_comments and edit/remove with update_finding_conversation_comment / delete_finding_conversation_comment.
Idempotent: add_conversation_to_bucket and add_conversation_to_finding return the existing row with alreadyExisted: true on re-add. The conversation must belong to the same agent as the bucket. create_bucket accepts only type: "static" today. AI agent creation runs the system prompt through the audit pipeline before insert. create_finding submits every finding as a candidate pending platform review: it does not appear in the organization’s findings list and is not auto-linked to calls until a platform admin approves it (or rejects it) from the Candidates queue on the findings page. update_agent changes only the fields you pass — send description: null to clear it, omit a field to leave it untouched. systemPrompt is accepted for AI agents only (human agents have none) and is written as-is: unlike creation, it is not re-run through the audit pipeline. An agent’s type and provider are immutable and cannot be changed.

Review queues

A queue is a saved set of filters defining a dynamic collection of conversations to review. These tools are MCP-only. The filters object accepts: agentIds, agentType (ai / human), durationMinSeconds, durationMaxSeconds, endedReasons, dateFrom (YYYY-MM-DD), dateTo (YYYY-MM-DD), alreadyReviewed, hasAudio, isInteresting, findingTypes. An empty/omitted filters matches every conversation in the org. On update_queue, passing filters replaces the whole object — it is not merged with the existing one.

Changes

A change is one atomic, agent-scoped suggestion that addresses a set of findings. Each new change starts as a draft (no workflow status, hidden from the main list) until a human publishes it. These tools are MCP-only.
For API stability the MCP tool names keep the original solution term (create_solution, list_solutions, …) even though the product now calls these changes. The tools and the UI operate on the same records.
create_solution inserts exactly one draft per call — never bundle multiple ideas into one body; call it again for each distinct suggestion. A published change’s status is one of backlog / todo / in_progress / done / cancelled. You can’t set status on a draft: move it out of draft first with publish_solution (→ backlog) or discard_solution (→ cancelled). Deleting a change removes its finding links by cascade but never deletes the underlying findings.

Reports

A report is a free-form analyst write-up authored in the same rich-text editor as findings and changes. prompt holds the question or brief; description holds the body as a TipTap/ProseMirror JSON document. status tracks the generation lifecycle (pending / generating / completed / failed) — a report you create sits in pending unless you pass a finished body and set completed. These tools are MCP-only. On update_report, passing description replaces the whole body — it is not merged with the existing document. Pass prompt: null to clear the ask.

Bucket population

These tools snapshot conversations into buckets from queues or raw filters. They are MCP-only. Because queues can span multiple agents but buckets are agent-scoped, create_bucket_from_queue creates one bucket per matched agent and returns an array. The populate_* tools are idempotent — re-running adds zero new rows — and matches belonging to a different agent than the bucket are skipped and counted in skippedOtherAgentCount.

Bucket & finding mutations

The remaining MCP-only mutations, plus the one finding-conversation unlink that does mirror a REST endpoint. type accepts only "static". Every delete cascades its own link rows (bucket tasks, finding-conversation links, comments) but never deletes the underlying conversations.

Provider import

Connect a call provider and pull its agents and calls directly into Zelto — the same connect → import → backfill flow as Settings → Integrations, driven from your editor. These tools are MCP-only and cover every pull-capable provider: Vapi, Retell, Speechify, Telnyx AI, ElevenLabs, and Kapso (WhatsApp). Each takes a provider argument (vapi / retell / speechify / telnyx / elevenlabs / kapso). connect_provider uses a browser handoff so the API key never passes through the agent: it returns a one-time url (valid 15 minutes). You hand that URL to the user; they open it while signed in to Zelto and paste their key there, so the key goes straight to the server — never into your context or logs. Don’t ask the user for their API key yourself. After they finish, poll check_provider_connection with the returned token until status is completed (it returns agentsFound) — or expired (start over). label defaults to Default and must be unique per provider; the user can change it on the connect page.
Never accept a provider API key as a tool argument. It would be captured in the agent’s conversation and client logs. connect_provider is a handoff for exactly this reason — the key only ever travels browser → server.
list_provider_agents returns every agent/assistant in the connected account(s), each annotated with alreadyImported (and importedAgentId once imported). agentId is the provider’s external id; for Vapi and Telnyx AI that is the assistant id; for Kapso it is the WhatsApp phone_number_id (each number is one agent — see Kapso). get_retell_agent reads one Retell agent directly without importing it. It returns the current general_prompt for Retell LLM agents and global_prompt for conversation-flow agents. import_provider_agent imports one agent and fails if it is already imported — call list_provider_agents first. It backfills the agent’s recent calls (sinceDays, default 14, max 30) and enrolls the agent in ongoing auto-import, so new calls keep flowing in without another pull. import_provider_conversations imports that agent’s calls from since (an ISO 8601 timestamp, e.g. 2026-03-01T00:00:00Z) until now, in the background — the agent must already be imported, and re-running with an overlapping window is safe (already-imported calls are skipped).

Onboarding

Stand a new organization up end to end from your editor: connect a call provider, create a monitor, and route findings. These tools are MCP-only. The Onboard with an agent guide walks an agent through the whole flow using them. A headless run in order:
  1. get_onboarding_status — read nextIncomplete to resume from the right step.
  2. connect_integration — provider is one of vapi, retell, speechify, telnyx, elevenlabs, kapso; apiKey is that provider’s key. For Vapi / Retell / Speechify the response’s webhook.webhookUrl must be pasted into the provider’s dashboard (a human step) before calls flow; ElevenLabs and Telnyx AI sync automatically, and Kapso registers its own webhook on each WhatsApp number you import with import_provider_agent. Confirm with list_conversations.
  3. create_monitor — pass a starterKey from list_starter_monitors, or a name + a natural-language boolean instruction. Omitting both agentIds and companyIds applies it to every current agent. Confirm with list_monitors. Tool-execution checks (tool_check — did submit_order actually fire) are authored in the Monitors editor; create_monitor still creates an AI yes/no rubric.
  4. set_findings_delivery — channels is any of email / slack (Slack also needs the Slack app connected via OAuth in the web UI).
These are write tools (except the list_* / get_* reads) and return a JSON object that may carry an error key — inspect it, and because writes settle asynchronously, confirm each step with the paired read tool before advancing.

Monitors & scorecards

Manage the AI judges that surface findings (create_monitor / list_monitors / list_starter_monitors are under Onboarding). Use list_monitors to find an ID, then call get_monitor with that ID as monitorId. It returns { monitor: { ...configuration, alert } }. The pipeline is resolved for legacy monitors too; timestamps (analysisStartAt, updatedAt) are ISO strings, and sourceFindingId identifies an originating finding when present. alert includes its enabled state, comparator, threshold, window, sustained days, Slack channel ID, and email recipients; it is null when no alert is configured. API keys are never returned; hasApiKey reports whether one is configured. Reads are uncached and scoped to your organization. Missing or other-organization IDs return a monitor-not-found error. Use get_monitor_results separately for evaluation outputs. Pass companyIds to create_monitor, create_pipeline_monitor, or update_monitor to target calls attributed to selected companies, including future agents. Companies must be enabled to create a company scope, IDs must belong to your organization, and at least one company is required. Do not pass agentIds together with companyIds. Omitting both on update preserves the saved scope; list_monitors exposes its scopeType and selected companies. Company edits change visible history without deleting stored results; missing historical evaluations still require opt-in analysis. Each ai_analyze stage in create_pipeline_monitor accepts useAgentPrompt and useCallContext (available CRM data); both default to false on new stages. To edit context, read stage IDs with get_monitor, then call update_monitor with stageContext: [{ stageId: "step-id", useAgentPrompt: false, useCallContext: true }]. Each patch needs a unique existing AI stage ID and at least one boolean option; omitted options are preserved. All supplied edits apply atomically. Context changes increment the monitor version for future evaluations and reset alert breach state; historical results stay intact. The response includes version and, when context is supplied, the updated pipeline.

Findings workflow

Beyond create_finding / update_finding, these drive the candidate → open lifecycle and dedup. Everything in this table except set_finding_version_scope requires the owner or admin role, as does delete_finding and renaming through update_finding — see Your role decides what you can do.

Call review

flag_call_for_review only queues a call; these record the actual review.

Agents (lifecycle)

create_ai_agent / create_human_agent / update_agent plus: merge_agents / unmerge_agents exist too but require an admin (cross-org) key, matching the staff-only merge tools in the web UI.

Receivers (phone numbers)

Integrations & connectors

Provider connect/import is under Provider import. These configure an already-connected integration. Slack (post-connect; OAuth install stays in the web UI): add_slack_channel, remove_slack_channel, set_default_slack_channel, set_slack_channel_chat_enabled, send_slack_channel_test, set_agent_slack_channel — each takes a Slack channel id (C…), and set_agent_slack_channel also an agentId. Linear (post-connect; OAuth install stays in the web UI): list_linear_teams, set_default_linear_team (teamId, nullable), create_linear_issue_for_finding (findingId), create_linear_issue_for_solution (solutionId), unlink_linear_issue (findingId? / solutionId?).

Organization & members

Every tool in this table except upload_conversation requires the owner or admin role, and update_member_role requires owner — see Your role decides what you can do. The inputs above are the primary fields; the MCP server advertises the full, authoritative schema for each tool via tools/list. Email inputs use regular-language JSON Schema patterns so the advertised tool schemas remain compatible with strict structured-output providers, including OpenAI.

Output shapes & pagination

Tool responses match the REST API exactly. See the API reference for the JSON shapes of Agent, Conversation, Transcript, Review, Bucket, and Finding, plus the { data, nextCursor } pagination envelope. limit clamps to 200 (default 50). cursor is opaque — pass back the nextCursor value from a previous response to fetch the next page.

Troubleshooting

  • 401 from every tool — the bearer token is wrong or revoked. Test the key directly: curl -H "Authorization: Bearer $ZELTO_API_KEY" https://api.zelto.ai/v1/agents. If that also returns 401, mint a new key.
  • This credential is read-only and cannot call "<tool>". — the OAuth token was granted the read scope only. Reconnect and grant write, or use an API key. See Read and write scopes.
  • 400 with Batched tool calls are not supported — the client sent a JSON-RPC array. Send one message per POST.
  • Only an organization owner or admin can perform this action. — the tool worked, your role didn’t. Your credential resolves to the member role and the tool administers the organization. Ask an owner or admin to run it or to raise your role; see Your role decides what you can do.
  • Tools list is empty in the client — most clients cache the tool list. Restart the client (Claude Code: /mcp; Cursor: toggle the server off/on; Codex: restart the CLI).
  • 429 rate-limited — back off and retry. Use the cursor pagination instead of fetching large pages.
  • Cross-org access denied — keys are scoped to the org they were minted in. Switch organizations in the web app and mint a new key from that org’s Settings → Integrations.

Recording access

Recording URL fields point to authenticated playback routes. Send the same organization API key or OAuth bearer token when fetching audio; browser playback requires a current signed-in membership. Both mixed and multi-channel recordings follow this rule. Managed recordings use redirects to signed URLs that expire after 15 minutes. Follow the redirect to play or download, and request the original authenticated route again when the signed URL expires. Historical public recording URLs stop working when private storage is enabled.