API reference
Build on CallDeskTech.
Build, publish and operate voice agents. Authenticate with Authorization: Bearer cdk_live_… (create keys in Settings → API Keys). A key is pinned to one workspace; call GET /me to get its tenant id.
Quick start
Send your key as a bearer token. Ask who it belongs to first: the response includes the workspace id used in the paths below.
curl https://calldesk.tech/api/v1/me \
-H "Authorization: Bearer cdk_live_..."curl -X POST \
https://calldesk.tech/api/v1/tenants/$TENANT/agents/from-template \
-H "Authorization: Bearer cdk_live_..." \
-H "Content-Type: application/json" \
-d '{"templateId": "medical-receptionist"}'Errors return 401 for missing or bad credentials, 404 for anything outside your workspace, and 429 when rate limited.
MCP server
Connect an AI assistant to your workspace. Add the URL, sign in when your browser opens, and pick the workspace to share. No API key needed.
claude mcp add --transport http calldesktech https://calldesk.tech/mcphttps://calldesk.tech/mcpEach connection is a workspace API key named after the app. Revoke it under Settings, API Keys. Clients that only support keys can send one as Authorization: Bearer cdk_live_....
MCP tools
Everything the MCP server exposes. Tools that cost money or delete data say so in their description, and assistants ask before calling them.
Workspace
whoamiShow the workspace this connection is pinned to.
Agents
flow_authoring_guideRead first: node types, parameters, edge rules and gotchas for building a flow.list_agentsList agents with their latest version’s engine and voice, and the numbers routed to them.create_agentCreate an agent (publish a version next).get_agentGet one agent.rename_agentRename an agent.delete_agentPermanently delete an agent and its versions, subflows and knowledge bases.list_agent_versionsList an agent’s immutable versions, newest first.publish_agent_versionPublish a new immutable version from a conversation-flow graph. Optional tier (standard or pro) picks the models for you; optional routingMode expert_backup (with acceptExpertBackup, Lite or Standard) adds the paid expert backup extra; advanced llmModel and ttsModel override them. Optional requireBookingTools fails the publish (422) when the flow books appointments but no calendar is connected; otherwise the result carries a warnings array.list_agent_templatesList the built-in agent templates.create_agent_from_templateCreate a ready-to-call agent from a template and publish its first version. The result may carry publishWarnings (e.g. no calendar connected).list_agent_environmentsList an agent’s staging and production environments and the version each points to.promote_agent_environmentPromote a version into staging or production.analyze_agent_copilotAnalyze recent real calls for recurring problems and propose flow edits grounded in specific transcripts.
Pricing and models
list_pricing_tiersList the pricing tiers (Lite, Standard, Pro) with price per minute, what each includes, carrier billing and the planned add-ons (coming soon, not purchasable yet).list_model_optionsList the language models (llmModel) and voice models (ttsModel) a version can use, with status and notes (advanced).
Subflows and knowledge
list_subflowsList subflows (library ones plus an agent’s own).create_subflowCreate a reusable sub-graph to reference from a subflow_ref node.update_subflowUpdate a subflow. Published versions keep their snapshot.delete_subflowDelete a subflow.list_knowledge_basesList knowledge bases.create_knowledge_baseCreate a knowledge base.add_knowledge_itemsAdd question-and-answer items to a knowledge base.delete_knowledge_baseDelete a knowledge base and its items.
Sounds
list_soundsList the workspace’s intro jingle and sound effects.create_jingleGenerate the intro jingle that plays when a call connects.create_sound_effectGenerate a sound effect the agent can play mid-call.delete_soundDelete a jingle or sound effect.
Numbers and calls
list_phone_numbersList phone numbers and the agent versions they route to. Numbers bought from us are a paid extra on every plan ($2.00 per month per number plus 1.5 cents per minute of inbound calls on Twilio, $1.00 plus 1 cent on Telnyx); bringing your own number is free.set_number_routingRoute a number’s inbound or outbound calls to a version or an environment.place_callPlace a real outbound call from one of your numbers (costs money).list_callsList recent calls.get_callGet one call: transcript, outcome, duration and transfer status.
Batch calls
list_batch_callsList batch calls.create_batch_callCreate (not start) a batch of outbound calls for a version.get_batch_callGet a batch and every target with its dial status.run_batch_callStart a batch: dials every number (costs money).
Webhooks and integrations
list_webhooksList webhooks.create_webhookRegister a webhook.delete_webhookDelete a webhook.lookup_hubspot_contactFind a caller’s HubSpot contact by phone and return fields usable as call-time variables.
Analytics
get_analyticsCall analytics by day.get_qa_overviewQA scores, resolution rate and transfer metrics.
Pricing tiers
The tier is the engine: pick one when you publish a version and we choose the right voice and intelligence for it. You never need to pick a model. Pass tier to POST /agents/{agentId}/versions or the publish_agent_version tool, and read the list from GET /pricing (no sign-in needed) or list_pricing_tiers.
Lite
$0.02 / min
An efficient engine with a clear, English-only voice.
- Voice
- Clear and efficient
- Response speed
- Good
- Reasoning
- Good
Standard
$0.05 / min
A fast, strong engine with a natural voice.
- Voice
- Natural
- Response speed
- Fast
- Reasoning
- Strong
Pro
$0.09 / min
Our strongest engine with our most expressive voice.
- Voice
- Most expressive
- Response speed
- Fast
- Reasoning
- Strongest
- Included on every plan: Call summary; Full transcript; Structured field extraction (name, reason for the call, and anything else you ask for); Call transfers; Keypad tones (DTMF); Knowledge base; Calendar booking; Call testing and live call monitoring; API and MCP server; 40+ languages (non-English languages need the Standard or Pro voice; Lite's voice is English only).
- Bring your own carrier on every plan, or add phone numbers from us. Phone numbers from us, on any plan: Telnyx number $1.00 per month plus 1 cent per inbound minute; Twilio number (premium carrier: supports payments and our existing SMS setup) $2.00 per month plus 1.5 cents per inbound minute. Bring your own number or carrier: free.
- Standard with one Telnyx number and 500 inbound minutes a month: 500 x 5c + $1.00 + 500 x 1c = $31.00.
- Expert backup on Lite and Standard: 1.5 cents more per minute while it is on, for better accuracy on hard turns. Publish with
"routingMode": "expert_backup"and"acceptExpertBackup": true(voiceEngine poc, tier lite or standard). - Optional add-ons are coming soon and cannot be bought yet; amounts will be announced when they launch. Planned: caller sentiment, every turn, advanced analytics, premium voice, long prompts.
- Lite uses our efficient voice, built for fast, high-volume calls. Publishing with
"tier": "lite"needs"acceptLowerQuality": trueto confirm you are choosing the Lite voice for the lower price. - Agents published without a tier keep their current per-minute price. Choosing a tier is optional.
curl -X POST https://calldesk.tech/api/v1/agents/$AGENT/versions \
-H "Authorization: Bearer cdk_live_..." \
-H "Content-Type: application/json" \
-d '{"flowName":"Front desk","startNodeId":"greet","nodes":[...],
"voiceEngine":"poc","tier":"standard"}'Booking without a calendar
An agent can only book appointments live when a calendar is connected under Integrations and globalSettings.calendarTools is not false. If you publish a flow that collects appointments without that, the version is still published and the response carries a warnings array with code booking_without_calendar: the agent takes a request and someone must confirm it. Templates installed from the API report the same warnings as publishWarnings.
If you would rather fail than ship a request-only agent, pass "requireBookingTools": true when publishing. In that case, and only that case, the publish is refused with HTTP 422 and code booking_requires_calendar (with reasons and fix) before anything is saved or billed. Flows without booking steps, or workspaces with a calendar connected, publish as normal.
Advanced: choose models yourself
You do not need this to use a pricing tier. For API and MCP users who want control, an agent version can also choose the language model that runs the conversation and the voice model that speaks it. Models you set override the tier’s choice, and the tier’s price does not change. Pass llmModel and ttsModel when you publish a version, or read the same list from GET /models or the list_model_options tool. Leave them out and the tier (or the default) chooses.
- llmModel applies to agents on the in-house voice engine (voiceEngine "poc"). If the engine cannot use the chosen model (for example its provider key is not configured) or the provider fails before the agent speaks, the default model answers, so the call still works.
- ttsModel must belong to the agent’s voice backend (ttsBackend). The kokoro, piper, and minimax backends have no model choice.
- Individual flow nodes can override the model with params.model.
- Most people never need this list: publish with a pricing tier (tier "standard" or "pro", see GET /pricing) and the right models are chosen for you. Models you set explicitly override the tier’s choice.
Language models (llmModel)
| Id | Status | Notes |
|---|---|---|
claude-haiku-4-5-20251001default | tested | The default. Fastest first response of the tested models (about 0.6 s) and the most reliable at recording fields and following the flow. |
claude-sonnet-4-6 | tested | Stronger reasoning for complex flows, costs more than Haiku and is slower. |
gpt-6-luna | tested | A much lower-cost option than Haiku. Passed our cooperative-caller benchmark 6 of 6, but responds slower (roughly 0.5 to 1.6 s) because turns that record a field need a second request. Not yet cleared on the stress tests (corrections, misheard numbers). |
gemini-3.1-flash-lite | preview | A low-cost, fast option from Google. Passed 36 of 40 in our text benchmark with a first response of about 0.75 s, but has not been tried on real calls yet. |
Voice models (ttsModel, with ttsBackend)
| Id | Backend | Notes |
|---|---|---|
eleven_multilingual_v2default | elevenlabs | The current default: natural, supports every language we offer. |
eleven_flash_v2_5 | elevenlabs | Lower cost than Multilingual v2 and the fastest to first audio. |
eleven_turbo_v2_5 | elevenlabs | Previous-generation low-latency model. |
eleven_v4_turbo | elevenlabs | The most expressive low-latency model. |
sonic-3.6default | cartesia | Cartesia’s default model. |
sonic-2 | cartesia | Previous generation. |
curl -X POST https://calldesk.tech/api/v1/agents/$AGENT/versions \
-H "Authorization: Bearer cdk_live_..." \
-H "Content-Type: application/json" \
-d '{"flowName":"Front desk","startNodeId":"greet","nodes":[...],
"voiceEngine":"poc","ttsBackend":"elevenlabs",
"ttsModel":"eleven_flash_v2_5","llmModel":"gpt-6-luna"}'What you pay per minute is set by your pricing tier (or, for agents published without one, the flat price of your voice backend), not by the models you choose. See Pricing tiers above.
Official SDKs
Client libraries that wrap this API. Both are thin, typed wrappers — the routes below are the source of truth.
pip install calldesktech
# or: pip install git+https://github.com/calldesktech/calldesktech-pythongit clone https://github.com/calldesktech/calldesktech-node
cd calldesktech-node && npm install && npm run build
# npm install calldesktech <- once publishedimport { CallDeskTech } from "calldesktech";
const client = new CallDeskTech({ apiKey: process.env.CALLDESK_API_KEY! });
const { agentId, versionId } = await client.agents.createFromTemplate({
templateId: "medical-receptionist",
});
const { phoneNumbers } = await client.phoneNumbers.list();
await client.phoneNumbers.setRouting(phoneNumbers[0].id, {
direction: "inbound",
agentVersionId: versionId,
});
await client.phoneNumbers.call(phoneNumbers[0].id, { toNumber: "+15551234567" });Account
/meWho am I?
For an API key, returns the tenant it is pinned to — use that id in the paths below.
Returns { auth, tenantId, tenantName }
Agents
/tenants/{tenantId}/agentsList agents
Each agent includes its latest version’s engine/voice and any routed phone numbers.
Returns { agents: Agent[] }
/tenants/{tenantId}/agentsCreate an agent
name*- string
mode- 'simple' | 'advanced'
Returns { agent }
/agents/{agentId}Get an agent
Returns { agent }
/agents/{agentId}Rename an agent
name- string
Returns { agent }
/agents/{agentId}Delete an agent
Also deletes its versions, subflows and knowledge bases.
Returns { success }
/agents/{agentId}/versionsList versions
Returns { versions: AgentVersion[] }
/agents/{agentId}/versionsPublish a new version
Versions are immutable. Set globalSettings.language (en default; es, fr, pt-BR, it, nl, hi, de, pl, id, ar) for a non-English agent: the engine switches speech recognition, the reply language and the voice, and a non-English poc agent is pinned to the ElevenLabs voice (billed at the ElevenLabs rate). nodes is the conversation-flow graph; subflow_ref nodes are embedded as snapshots at publish time. The response may include a non-blocking warnings array of { code, message }: booking_without_calendar means the flow collects appointments but the workspace has no calendar connected (or globalSettings.calendarTools is false), so the agent takes a request instead of booking. The version is still published. Set requireBookingTools: true to make that case an error instead: the request is refused with HTTP 422 and code booking_requires_calendar (plus reasons and fix) before anything is saved or billed. When the flow has no booking intent, or a calendar is connected, the flag changes nothing.
flowName*- string
startNodeId*- string
nodes*- FlowNode[]
globalSettings- object
requireBookingTools- boolean (optional, default false; true refuses the publish with 422 code booking_requires_calendar when the flow collects appointments but no calendar is connected or globalSettings.calendarTools is false; nothing is saved or billed)
voiceEngine*- 'poc' | 'retell'
voiceId- string
tier- 'lite' | 'standard' | 'pro' (optional; poc engine only; see GET /pricing; 'lite' also needs acceptLowerQuality: true, to confirm you are choosing its efficient, lower-cost voice; the tier picks the language model and voice for you; omit to keep the flat per-minute price of the voice backend)
routingMode- 'expert_backup' (optional; Expert backup: hands the hard turns to a stronger model for better accuracy on hard turns; 1.5 cents per minute extra while on; poc engine with tier lite or standard only, not pro; needs acceptExpertBackup: true; omit for standard routing)
acceptExpertBackup- boolean (required true with routingMode expert_backup; without it you get 400 code expert_backup_acceptance_required with the terms)
ttsBackend- 'kokoro' | 'elevenlabs' | 'cartesia' | 'minimax' | 'piper' (advanced)
llmModel- string (advanced, optional; poc engine only; overrides the tier; see GET /models; default claude-haiku-4-5-20251001)
ttsModel- string (advanced, optional; elevenlabs or cartesia only; overrides the tier; see GET /models)
Returns { version, flow, warnings?: { code, message }[] }. 422 { error, code: "booking_requires_calendar", reasons: string[], fix } when requireBookingTools is true and the agent cannot book
/pricingList pricing tiers
The pricing tiers (Lite, Standard, Pro) and planned optional add-ons (coming soon, not purchasable yet), with price per minute, the engine behind each tier (ratings for voice, response speed and reasoning), what every plan includes, and the phone number extra. Every tier is bring-your-own carrier. No authentication needed. Pass a tier id as tier when publishing a version. Lite is coming_soon and cannot be selected yet. Agents published without a tier keep the flat per-minute price of their voice backend. On every plan you can also buy a phone number from us as a paid extra (premium Twilio carrier: $2.00 per month per number plus 1.5 cents per minute of inbound calls; Telnyx carrier: $1.00 per month plus 1 cent per minute of inbound calls; outbound uses your own carrier); bringing your own number or carrier is free. The response also lists the expert backup extra (expertBackup: price, tiers, terms). Add-ons cannot be bought yet; their amounts are null until they launch.
Returns { currency, unit, tiers: { id, name, pricePerMinuteCents, pricePerMinuteDollars, tagline, whoItsFor, ratings: { voice, responseSpeed, reasoning }, carrierMode: 'byo' | 'managed', availability: 'live' | 'coming_soon' }[], addOns: { id, label, description, centsPerMinute | null, proposed, defaultOn }[], includedOnAllTiers, carrierNote, notes, phoneNumbers, expertBackup: { id, label, description, availability, tiers, centsPerMinute, publishFields, terms } }
/modelsList model options
The language models (llmModel) and voice models (ttsModel) you can choose when publishing a version, each with a tested or preview status and notes on speed and reliability. If the voice engine cannot use the chosen model, or the provider fails before the agent speaks, the default model answers, so a call never fails because of this setting. A flow node can override the language model with params.model.
Returns { defaults: { llmModel }, llmModels: { id, label, provider, default?, status, notes }[], ttsModels: { id, backend, label, default?, notes }[], notes: string[] }
/agent-templatesList agent templates
Built-in templates (receptionist, medical receptionist, payment collection, IVR navigation and more) that can be installed as an agent.
Returns { templates: { id, label, description, category, defaultVariables: {name: value}, variables: string[] (the {{placeholders}} you can set on install) }[] }
/tenants/{tenantId}/agents/from-templateCreate an agent from a template
Creates the agent, its subflows and knowledge base, and publishes version 1. voiceEngine "poc" runs on CallDesk; "retell" also creates the equivalent Retell conversation-flow agent (some node types are approximated; see warnings). transferTo and functionUrl fill empty transfer numbers and function webhooks. variables sets the template's {{placeholders}} (see GET /agent-templates); business_name defaults to the tenant name.
templateId*- string (from GET /agent-templates)
name- string
voiceEngine- 'poc' | 'retell'
transferTo- E.164 string
functionUrl- https URL
calendarTools- false to turn off live calendar lookups and bookings
language- agent language: 'en' (default), 'es', 'fr', 'pt-BR', 'it', 'nl', 'hi', 'de', 'pl', 'id' or 'ar'
variables- object, e.g. {"business_name": "Acme Dental", "agent_name": "Sam"}
Returns { agentId, versionId, versionNumber, template, voiceEngine, retellAgentId?, warnings? (Retell conversion notes, string[]), publishWarnings?: { code, message }[] (e.g. booking_without_calendar) }
/agents/{agentId}/environmentsList an agent's environments
Every agent has "staging" and "production", each pointing at the version it currently runs (null if nothing has been promoted into it yet).
Returns { environments: { id, name, version_id, updated_at }[] }
/agents/{agentId}/environments/{name}/promotePromote a version into staging or production
Every phone number (or batch call) routed to this environment picks up the new version immediately — no need to re-route. Rolling back is promoting an older version again.
versionId*- string
Returns { environment, resynced }
Subflows
/tenants/{tenantId}/subflowsList subflows
?agentId Only library subflows plus this agent’s own
Returns { subflows: Subflow[] }
/tenants/{tenantId}/subflowsCreate a subflow
name*- string
scope- 'agent' | 'library'
agentId- string (agent scope)
nodes- FlowNode[]
startNodeId- string
Returns { subflow }
/tenants/{tenantId}/subflows/{subflowId}Get a subflow
Returns { subflow }
/tenants/{tenantId}/subflows/{subflowId}Update a subflow
Already-published versions keep the snapshot they embedded.
name- string
nodes- FlowNode[]
startNodeId- string
scope- string
Returns { subflow }
/tenants/{tenantId}/subflows/{subflowId}Delete a subflow
Returns { ok }
Knowledge bases
/tenants/{tenantId}/knowledge-basesList knowledge bases
Returns { knowledgeBases }
/tenants/{tenantId}/knowledge-basesCreate a knowledge base
Set agent_id — a knowledge_base node only sees content from a KB attached to its agent.
name*- string
source_type*- 'website' | 'pdf' | 'manual'
source_url- string
agent_id- string
Returns { knowledgeBase }
/knowledge-bases/{knowledgeBaseId}Rename or re-attach to an agent
name- string
agent_id- string | null
Returns { knowledgeBase }
/knowledge-bases/{knowledgeBaseId}Delete a knowledge base
Returns { success }
/knowledge-bases/{knowledgeBaseId}/itemsList Q&A items
Returns { items }
/knowledge-bases/{knowledgeBaseId}/itemsAdd Q&A items
items*- { question: string, answer: string }[]
Returns { items }
Sounds
/tenants/{tenantId}/call-audioList jingle and sound effects
Returns the workspace's jingle and sound effects (asset_type is jingle or sound_effect); enabled: false rows are ones that were replaced.
Returns { assets }
/tenants/{tenantId}/call-audioGenerate a jingle or sound effect
Generates the sound from a text description, then stores it. Spends generation credits. A workspace has one jingle and up to 10 sound effects; a new jingle replaces the current one, and reusing a sound effect name replaces that effect. durationSec is 1-12. A sound effect needs a description saying WHEN the agent should play it: the agent reads it. Only plays on agents running the in-house engine; Retell agents are unaffected. Can take up to a minute.
type*- 'jingle' | 'sound_effect'
name*- string (letters, numbers, _ and -)
description- string (required for sound_effect)
prompt*- string (what the sound should be, max 500 chars)
durationSec*- number (1-12)
Returns { asset }
/tenants/{tenantId}/call-audio/{soundId}Delete a jingle or sound effect
Callers stop hearing it immediately.
Returns { ok }
/tenants/{tenantId}/call-audio/{soundId}/audioPreview a sound
Returns a playable WAV (audio/wav) of exactly what callers hear.
Returns audio/wav
Phone numbers
/tenants/{tenantId}/phone-numbersList phone numbers
Returns { phoneNumbers }
/tenants/{tenantId}/phone-numbers/purchaseBuy a phone number
On every plan this is a paid extra. Choose the carrier: twilio (premium) is $2.00 per month per number plus 1.5 cents per minute of inbound calls to your purchased numbers; telnyx is $1.00 per month plus 1 cent per minute of inbound calls; outbound calling uses your own carrier. Pass acceptNumberAddOn: true to accept the terms (without it you get 400 code number_addon_acceptance_required with the terms). Billing is set up before the number is bought and undone if the purchase fails. Bringing your own number is free. Requires a payment method on file.
areaCode- string (optional)
carrier- 'twilio' (default) | 'telnyx'
acceptNumberAddOn- boolean
Returns { phoneNumber }
/phone-numbers/{phoneNumberId}Release a phone number
Removes the number from your workspace. A number bought from us is released on its carrier first, and its monthly charge drops by one number (the charges end when you hold none). A number you registered yourself is only unregistered.
Returns { deleted: true }
/phone-numbers/{phoneNumberId}/routingRoute a number to an agent version or environment
Pass exactly one of agentVersionId (a specific version, direct pin) or environmentId (staging/production — the number always runs whatever version that environment currently points to, so promoting later needs no further call here).
direction*- 'inbound' | 'outbound'
agentVersionId- string | null
environmentId- string
Returns { phoneNumber }
Calls
/phone-numbers/{phoneNumberId}/callPlace an outbound call
Calls toNumber from this number using its outbound agent. Rate-limited per workspace (429).
toNumber*- E.164 string
Returns { call: { sid, to } }
/tenants/{tenantId}/callsList calls
Each call includes analysis.issues (problems found automatically after the call; see Get a call).
?limit default 50
Returns { callLogs: CallLog[] }
/calls/{callId}Get a call
Includes transcript, outcome, duration, transfer status, and analysis (post-call analysis fields, null unless configured on the agent). analysis.issues lists problems found automatically after the call, each { code, severity: 'high'|'medium'|'low', message, evidence: string[], fix }; codes: claimed_booking_without_tool, placeholder_read_aloud, number_readback_mismatch, no_fields_collected, long_silence. The key is absent until the call has been checked and [] when nothing was found. Blocked and internal-test calls are never checked.
Returns { callLog }
/calls/{callId}/recordingStream a call recording
Returns audio
Batch calls
/tenants/{tenantId}/batch-callsList batch calls
Returns { batches }
/tenants/{tenantId}/batch-callsCreate a batch
phoneNumbers accepts either a plain list (newline/comma/semicolon-separated) or a CSV with a header row — a column named phone/phone_number/to/to_number/number is the recipient, every other column becomes a per-call dynamic variable (e.g. a "first_name" column lets the agent say {{first_name}}). Omit scheduledAt to leave the batch pending for a manual/API "run" call now; set it to a future ISO timestamp to have the platform run it automatically at that time instead.
agentVersionId*- string
phoneNumbers*- string (plain list) or CSV text with a phone/phone_number/to/to_number/number column plus optional variable columns
name- string, optional label
scheduledAt- ISO datetime, optional — future time to run automatically instead of on manual/API trigger
callTimeWindow- optional { timezone: IANA string, days: number[] (0=Sun..6=Sat), start_hour: 0-23, end_hour: 1-24 } — restricts when this batch is allowed to dial
Returns { batch }
/batch-calls/{batchId}Get a batch and its targets
Each target includes its phone number, dial status, dynamic_variables used, and call_log_id once placed.
Returns { batchCall, targets }
/batch-calls/{batchId}/runStart a batch
Dials paced by the platform-wide and per-workspace rate limits. Returns 409 if the batch is scheduled for later and not yet due, or outside its callTimeWindow — retry after that time.
Returns { started, ... }
Webhooks
/tenants/{tenantId}/webhooksList webhooks
Returns { webhooks }
/tenants/{tenantId}/webhooksRegister a webhook
Events: call.started (Retell-engine calls only), call.completed (includes analysis when configured), call.transferred, call.analyzed (post-call analysis results). Deliveries are signed with the returned whsec_ secret (X-CallDesk-Event header names the event).
url*- https URL
events- ('call.started' | 'call.completed' | 'call.transferred' | 'call.analyzed')[]
Returns { webhook }
/tenants/{tenantId}/webhooks/{webhookId}Update a webhook
url- string
events- string[]
enabled- boolean
Returns { webhook }
/tenants/{tenantId}/webhooks/{webhookId}Delete a webhook
Returns { success }
/tenants/{tenantId}/webhooks/{webhookId}/testSend a test delivery
Returns { ok }
Contacts
/tenants/{tenantId}/contactsList contacts
Returns { contacts }
CRM
/tenants/{tenantId}/crmList CRM connections
Access/refresh tokens are never returned — write-only, like API keys.
Returns { connections: { id, provider, provider_account_id, expires_at, created_at }[] }
/tenants/{tenantId}/crmDisconnect a CRM
?provider 'hubspot' | 'salesforce'
Returns { success }
/tenants/{tenantId}/crm/hubspot/connectStart HubSpot OAuth connect
Browser-navigation endpoint (not JSON) — 302s to HubSpot's authorize screen. Owner/admin only.
Returns 302 redirect
/tenants/{tenantId}/crm/hubspot/lookupLook up a caller in HubSpot (CRM→us)
On-demand lookup by phone for call-time personalization, e.g. injecting {{crm_company_name}} as a dynamic variable. No background sync.
?phone E.164 phone number
Returns { variables: { crm_contact_found, crm_first_name?, crm_last_name?, crm_company_name?, crm_email?, crm_job_title? } }
Analytics
/tenants/{tenantId}/analyticsCall analytics by day
?days 7 | 30 | 90
Returns { series, totals }
Quality
/tenants/{tenantId}/qa/overviewQA scores, resolution and transfer metrics
?days 7 | 30 | 90
Returns { avgScore, resolutionRate, transferSuccessRate, ... }
/agents/{agentId}/test-casesList simulation test cases
Returns { testCases }
/agents/{agentId}/test-casesCreate a test case
name- string
persona- string
successCriteria- string
Returns { testCase }
/agents/{agentId}/test-cases/{testCaseId}/runRun a simulation
Returns { passed, transcript, reasoning }
/agents/{agentId}/copilot/analyzeList Copilot suggestions
Returns { suggestions }
/agents/{agentId}/copilot/analyzeAnalyze recent calls for flow-edit suggestions
Looks at this agent's recent real calls (transcripts + QA critiques, whichever number is currently routed to it) for recurring problems and proposes concrete node-level prompt edits, each grounded in specific call transcripts. Requires at least 5 usable calls, otherwise returns a not_enough_history status instead of guessing. Never writes to the flow — suggestions are persisted as pending and must be accepted or dismissed via the suggestions route.
Returns { status, callCount, summary, suggestions: { id, node_id, current_text, suggested_text, rationale, supporting_call_ids, status }[] }