New Contacts, call tags and user status API endpoints
CloudTalk now offers many new API endpoints. From your own systems you can manage contacts (create, update, bulk upsert, search and tag them), tag your calls, and read and set your agents’ status.- Contacts: List and search contacts, Create a contact, Look up contacts, Bulk upsert contacts, Get a contact, Update a contact, Delete a contact, Add tags to a contact, Remove tags from a contact
- Contact tags: List contact tags, Create a contact tag, Rename a contact tag, Delete a contact tag
- Contact fields: List contact fields
- Call tags: List call tags, List a call’s tags, Add tags to a call, Remove tags from a call
- User status: List user statuses, Get a user’s status, Set a user’s status, List user status types
Know whether your messages arrived
The newmessage.delivery_updated
event tells you when the delivery status of a message sent from your
CloudTalk number changes: queued, sent, delivered, not_delivered,
failed, and seen for WhatsApp read receipts.- Why it failed: on
not_deliveredandfailed,error.codegives the reason to act on (for exampleinvalid_recipientorblocked), anderror.messagea sentence you can show your users. - Pair it with
message.sent: the event carries the status change only. Match it to the message onmessage_id, the same value asidonmessage.sent. - Which messages: messages sent from your CloudTalk numbers. Messages you receive have no delivery status.
Choose where a VoiceAgent’s calendar invite goes
When a VoiceAgent books on Calendly or Cal.com, the booking skill now names the variable the calendar invite takes the caller’s e-mail from, instead of the on/off “Send the calendar invite to the caller’s e-mail” setting. It starts ascontact.emails, the e-mails of the contact the call is about; an agent that
had the setting on keeps sending the invite there.- Your own variable: set the booking skill’s invite e-mail to a variable
you send in
call_properties.system_prompt.variablesof Initiate VoiceAgent call, for example{{customer_email}}in the dashboard for acustomer_emailyou send, and the invite goes to the first valid e-mail in it (a single address, several separated by commas, or a list). Sending the variable alone does not change the skill: it keeps the variable it is set to. Outbound calls can then book without a CloudTalk contact. - Your
contact.*values now reach the invite too: a non-emptycontact.emailsyou send replaces the contact’s e-mails for the invite, as it already did for what the agent is told.ct_call_context.contactin the analysis still shows the contact. - Inbound calls carry only contact fields, so a variable of your own is always empty there and the agent offers the booking-link SMS instead.
- No e-mail, no booking in the call: when the variable is empty or holds no valid address, the agent does not book an event type that needs an e-mail and sends the booking link by SMS, if it has that scenario.
booking_failed scenario
trigger, which the API already accepted, and says that a booking skill on
Cal.com or Calendly is set up in the dashboard: the API reads it but refuses
to write it.Outbound VoiceAgent calls know the contact they call
An outbound call started with Initiate VoiceAgent call now looks up the dialed number among your CloudTalk contacts, the same way an inbound call looks up the caller. When several contacts share the number, the first match is used. When none matches, or the lookup fails, the call goes ahead without a contact, as before.- Contact variables:
{{contact.*}}variables in the prompt, greeting, skills and scenarios, and custom tools that reference contact fields, now receive the matched contact’s values on outbound calls. Before, they were empty. A call without a contact still gets empty values, unless you send your own (below). - Your own values still win: a non-empty value you send for a
contact.*variable incall_properties.system_prompt.variables(as a nested"contact": {"name": …}object or a flat"contact.name"key) replaces the matched contact’s value. An empty or blank value,null, or an empty array or object does not: the contact’s value is used. Your value changes what the agent is told, what it sends to custom tools and, for the variable the booking skill takes the invite e-mail from, where the calendar invite goes. The analysis’ct_call_context.contactstays the matched contact’s. - Post-call analysis: the analysis of an outbound call, including the
Extract Information webhook payload, now carries the matched contact in
ct_call_context.contact(id,name,emails), as it already did for inbound calls. - Calendar invite: when the agent books on Calendly or Cal.com with the
booking skill’s invite e-mail left at
contact.emails, the matched contact’s first valid e-mail address receives the invite on outbound calls too.
{{contact.*}} variables, check that the
contact’s values are what you want the agent to say.Tell a VoiceAgent call which contact it is calling
Initiate VoiceAgent call accepts an optionalcontact_id: the CloudTalk contact or lead being called,
as a string of digits or an integer. The call then uses exactly that contact
instead of the first contact matched by the dialed number, which helps when
several contacts share a number.- What the contact is used for: the
{{contact.*}}variables, thect_call_context.contactof the post-call analysis, and the calendar invite while the booking skill takes the invite e-mail fromcontact.emails. A non-emptycontact.*value you send incall_properties.system_prompt.variablesstill wins for that variable in what the agent is told, what it sends to custom tools and, for the invite variable, where the invite goes; the analysis keeps the contact named bycontact_id. - New errors, only when you send
contact_id:422witherrors[].messagecontact_not_found(locationbody.contact_id) when the contact does not exist in your company, belongs to another company or was deleted;503contact_lookup_unavailablewhen the contact could not be looked up. In both cases no call is queued, so a503is safe to retry. Requests withoutcontact_idbehave as before.
200 with a session_id (not 204), a refused call answers 403
(deny or feature_not_enabled), and the 400, 403, 404 and 500
errors are application/problem+json with their codes in
errors[].message: too_many_properties, property_too_large,
invalid_call_number, voice_agent_not_found.500 errors now carry the fixed code internal_error instead of internal
error text.
Stop outbound VoiceAgent calls
errors are documented as the application/problem+json it returns.Every MCP action runs on its first call
Tools that send, dial, or change data no longer return a preview and wait for a second call with"confirm": true. Each one acts on its first call,
and your AI app’s approval prompt is the only approval, so you approve an
action once instead of twice. Tools that reach a person or overwrite or
delete data are marked Asks every time in the
tool reference: Claude and ChatGPT ask before each
call by default, unless an admin or user lets the tool run without asking.Breaking for your own code: a call that still sends confirm, or
expected_digest on cloudtalk_replace_campaign_associations, is refused
and does nothing. Remove the argument and call the tool once.ChatGPT workspaces: ChatGPT keeps a snapshot of a custom app’s tools
from when an admin approved it, so it keeps the old confirm input and
descriptions until the snapshot is refreshed. A workspace admin opens
Workspace settings → Apps, picks the CloudTalk app’s ⋯ menu, then
Action control → Refresh, and reviews and publishes the update. If
Refresh fails, remove the app and add it again.Receive a call’s post-call data in one webhook
The newcall.completed event
sends a call’s post-call data as one event: the call details, the recording,
the transcript reference, Conversation Intelligence results, the data an AI
Voice Agent extracted, and the Dialer disposition and survey. Subscribe to it
like any other event type; you don’t need to subscribe to the individual
events.- When: once per call, usually a few minutes after it ends: as soon as
every part the call can have has arrived, or 30 minutes after the call
ended, whichever comes first. Parts a call can’t have, such as
voice_agenton a call no AI Voice Agent handled, are markednot_applicableinstead of waited for. Data that arrives later is not added, and the event is not sent again. - What is included:
sourcesgives each part’s status (ready,not_applicable,timed_outorfailed), and a part that is notreadyis left out. - Size: each delivery is at most 64 KB. Anything shortened or left out to
fit is listed in
truncated. - Not in v1: a recording URL and an insights URL. Use the API by
call_id.
Connect an AI assistant by signing in with CloudTalk
The CloudTalk MCP server now accepts OAuth sign-in next to API keys. Addhttps://mcp.cloudtalk.io as a custom connector in Claude (Claude.ai, Claude
Desktop, Claude mobile), Claude Code, ChatGPT or Gemini, sign in with a CloudTalk
admin account and approve the access: no key to create or paste, and the
assistant acts as the admin who signed in. ChatGPT and Gemini can connect for
the first time. API keys keep working as before. See the
MCP Quickstart.- Only users with the Admin role can sign in.
- An API key that the server refuses now answers
403instead of401. A401means no credentials, or an expired sign-in, and carries aWWW-Authenticatechallenge that points clients to CloudTalk’s sign-in.
Manage webhook endpoints from the API
You can now manage webhook endpoints, their signing secrets and individual deliveries athttps://api.cloudtalk.io/v1/webhooks, with the same API keys as
the rest of the API. Endpoints created in the API and in the Dashboard are the
same endpoints. Recovering every failed delivery since a chosen time stays in
the Dashboard. See Manage webhooks with the API.- Endpoints: list,
create,
get,
update and
delete, plus
the event types you can
subscribe to. A create returns the signing secret and accepts an
Idempotency-Key: retrying with the same key and the same body returns the endpoint it already made instead of creating a second one. - Signing secrets: read
and rotate. A rotation
returns the new secret and
previousSecretExpiresAt: for 24 hours every delivery is signed with both secrets. - Deliveries: send a test event, list deliveries, get one with its payload and every attempt, and retry a delivery. Payloads are kept for 30 days.
https:// on a public host, and a company can have up
to 50 endpoints, Dashboard ones included. Test events and retries share a
limit of 10 per minute per company.Ask your assistant how a campaign is performing
The CloudTalk MCP server has a new read-only tool,cloudtalk_get_campaign_statistics.
It returns a campaign’s current contact funnel, plus dialed and answered
counts, answer and conversion rates, the person / voicemail / machine split,
the disposition breakdown and call durations, all-time or for a date window.
It serves the same figures as
Get a campaign’s statistics.- The Analytics profile now covers campaigns.
https://mcp.cloudtalk.io/analyticsincludes the new tool andcloudtalk_list_campaigns, so a reporting assistant can find a campaign and read its results. The Campaigns profile has the new tool too. - Read-only, aggregate figures. No contact, agent or call is identified, and like every tool it only reaches your own company’s campaigns.
- Requires the new Sales Dialer add-on, like the other campaign tools.
Choose a voice agent’s provider and model directly
This is a breaking change to Create a voice agent, Get a voice agent and Replace a voice agent.resolvedProviderandresolvedLlmare removed. Read the newproviderandllmattributes instead.llmis the canonical model id, such asopenai - gpt-4o-mini, not a{provider, model}object.providerOverrideandllmOverrideare deprecated and no longer returned. Writes still accept them as aliases ofproviderandllm; when a request sends both, the alias wins.- The server no longer changes an agent’s provider or model on its own.
It picks them once at create. A replace that omits
provider,llmorminorVersionkeeps the stored value, and linking a knowledge base leaves them as they are.
provider(elevenlabsordeepgram) andllmare writable and returned on every read, asnullwhen the agent has none set. Anllmthe provider does not offer is rejected with422and the detail codeLLM_OVERRIDE_NOT_AVAILABLE_FOR_PROVIDER.minorVersionis writable. A create takes 0 to the latest; a replace takes the stored value or the latest.optimizedSettings(read-only) lists the values CloudTalk recommends for the agent’s current setup:isAvailable, thechangesas{path, from, to}, andeffects. To apply them, set eachpathto itstovalue and replace the agent.
optimizedSettings then
proposes ElevenLabs. Any language, primary or secondary, that the agent’s
provider does not serve is rejected with 422 and the detail code
LANGUAGE_NOT_SUPPORTED.The MCP voice-agent tools take provider and llm, with the old names kept
as deprecated aliases. cloudtalk_update_voice_agent merges the fields you
pass onto the stored agent. See MCP tools.Archived dispositions can no longer be used on campaign buttons
This is a behaviour change. When you remove a disposition that calls already carry, CloudTalk archives it instead of deleting it. Archived dispositions are now treated like deleted ones everywhere a disposition can be picked:- Get a campaign’s disposition buttons leaves out a button whose call tag is archived. Previously it was returned with its normal name, but an outcome recorded with it was never attached to the call.
- Replace a campaign’s disposition buttons
rejects an archived
tag_idwith422, the same as an unknown one. - List disposition tags no longer lists archived dispositions.
Buttons for a deleted disposition are no longer returned
This is a behaviour change to Get a campaign’s disposition buttons.- A button whose call tag has been deleted is left out of the response.
Previously it was returned with
name,colorandoutcomeset tonull. Deleting a disposition in your account settings now removes it from every campaign, as the settings page states. nullresolved fields now mean the call tags could not be read. While that lasts, every button is returned withname,colorandoutcomeset tonull.coloris alsonullfor a tag that has no colour.
Get real-time events pushed to your endpoint
CloudTalk’s account-level webhooks are documented. Register an HTTPS endpoint under Account > Webhooks and CloudTalk sends it a signed JSONPOST the
moment something happens on your account, so you no longer have to poll for
changes. Other CloudTalk features that call an endpoint you own, such as the
Call Flow Designer Webhook step or an AI Voice Agent’s Send via Webhook
option, use their own delivery path and are unaffected.- Overview covers setup, the shared event envelope, and the full catalogue of 22 event types.
- Verify signatures shows how to authenticate every delivery, in Node, Python, PHP and Go.
- Delivery and retries documents the retry schedule, automatic endpoint disabling, replay, and 30-day payload retention.
- Event references for calls, contacts, users, messages, Conversation Intelligence, AI Voice Agents and Dialer give every payload field with an example.
An undecodable pagination cursor is now a 400
This is a behaviour change, shipping in the next Dialer release. It affects every cursor-paginated Dialer list: List dialer campaigns, List a campaign’s contacts, List call scripts and List surveys.Breaking changes- A
cursorthe Dialer cannot decode is rejected instead of ignored. It now answers400withcode: "PAGINATION.INVALID_CURSOR". Until this release such a cursor was ignored and the endpoint returned200with the first page — the behaviour the 2026-09-02 entry below documented, and which that entry described as deliberate.
meta.nextCursor is null therefore read
page one for ever when its stored cursor was truncated or mangled, and if it
stopped, it recorded a partial sync as a complete one. A 400 makes that
failure visible on the request that caused it.Nothing changes for a correct paging loop. Pass meta.nextCursor back
exactly as you received it and you will never see this error. An absent or
empty cursor is still not an error — it means “start from the first page”.If you persist cursors between runs, treat the 400 as “my stored cursor is no
longer usable”: discard it and either restart the walk from a checkpoint you
control, or fail the run so an operator can recover it. The
Pagination guide carries the full recipe.Deleted campaigns leave the default campaign list
This is a behaviour change, shipping in Dialer release v1.0.22. Check any code that pages through List dialer campaigns before that release rolls out.Breaking changes- An unfiltered campaign list no longer returns deleted campaigns.
Campaigns whose status is
deletedare excluded when you send nostatusfilter. Until v1.0.22 they were included, which is what the previous entry on this page documented — that was the behaviour at the time, and v1.0.22 changes it so the list matches what Delete a dialer campaign already promises: a deleted campaign drops out of the listings while its history stays available for reporting.
include_deleted=true— a new optional boolean query parameter, defaultfalse. Set it totrueand the list returns campaigns of every status,deletedincluded. Only the literal stringstrueandfalseare accepted; anything else is a400.status=deleted— unchanged, and still the way to list only deleted campaigns. Whenstatusis set,include_deletedhas no effect.
include_deleted=true — or status=deleted if you only want the
deleted ones — to those requests.Nothing else about the endpoint changed in v1.0.22: cursor, limit,
status and mode behaved exactly as before, as did reading a single
campaign by id. (cursor has since changed — see the 2026-09-07 entry
above.)Stricter validation on the Dialer, and four corrections to these pages
These changes ship in Dialer release v1.0.21. Requests the API used to accept, or used to fail on with a500, now come back as a clear 4xx. Check
your integration against the list below before that release rolls out.Breaking changes- A whitespace-only
nameis rejected.nameis now trimmed before it is validated on campaign, call-script and survey creates and updates, so" "is exactly as empty as""and answers400withname should not be emptyinsubErrors. It previously created a resource whose name rendered blank everywhere it was shown. schedule_policy.start_dateandstart_timeare format-pinned.start_datemust be a real calendar date inYYYY-MM-DD,start_timea validHH:MMorHH:MM:SS; anything else, including a date that does not exist such as2026-02-30, is a400naming the field, for exampleschedule_policy.start_date must be a real calendar date in YYYY-MM-DD. A31-12-2026typo used to be stored and silently left the campaign with no scheduled start at all.- Export filters are validated. On
Request a campaign data export,
filters.contactIdandfilters.userIdmust be1or greater, andfilters.dateFrom/filters.dateTomust be strict ISO 8601 — otherwise400. A window whosedateTois not after itsdateFromnow answers422withdateTo must be after dateFrom, and a bound that clears the strict ISO 8601 check but falls outside the published RFC 3339date-timeshape (the week date2026-W33, for one) answers422withdateFrom must be a parseable ISO 8601 date-time— in both cases no job is enqueued; such requests used to be accepted and complete with a header-only file that looked like “no data”. Supplying only one bound is still valid: a missing end resolves to the time the job runs, a missing start to the campaign’s creation. - The unmatched-route
404body changed. A request to a path the Dialer does not route —DELETE /v1/dialer/surveys/{surveyId}, for instance — now answers the standard error envelope with the fixed messageRoute not foundand acorrelationId. It no longer echoes an internal path or your query string back to you. Every error the Dialer service itself raises carries acorrelationId, this one included — errors answered before the request reaches the service, such as the auth layer’s401, do not.
- A malformed
jobIdis a400, not a500. Get an export job’s status and Get a download link for a completed export answer400withValidation failed (uuid is expected)and acorrelationIdwhenjobIdis not a UUID. Both operations now document that400.
- List dialer campaigns
returns soft-deleted campaigns. The page said they appear only when you
filter with
status=deleted. They do not: an unfiltered list returns every status,deletedincluded. Passstatusif you need them left out. - Policy bundles are not stored verbatim. The
calling_policydescription said a bundle read back may carry knobs added by a newer release. Unknown keys are dropped on write, so only the documented knobs are ever stored. - An unknown
outcome_kindis a400, not a422. On Replace a campaign’s disposition buttons, enum and shape violations are400. The422is for atag_idthat is not a call tag on your account; a duplicatedtag_idis409. - Association ids are not checked for existence.
Replace a campaign’s associations
accepts any
ref_idfor an agent, target tag or queue group without verifying that it exists — a nonexistent target tag is stored and simply contributes no contacts. Validate the ids on your side.
cursor
the Dialer cannot decode was not an error — the list restarted from the
first page with 200. This no longer holds: as of the 2026-09-07 entry
above, such a cursor is a 400. The
Pagination guide documents the current contract.Connect AI assistants to CloudTalk
The CloudTalk MCP server is documented. Connect Claude, Cursor, or any MCP-compatible client to your account and work with your calls, contacts and AI Voice Agents in natural language.- Overview covers what MCP is, the tool profiles, and what each one is built for.
- Quickstart gets a client connected in five minutes.
- Tool reference lists every tool with its parameters, defaults and limits.
The Dialer partner API uses your standard API keys
The Dialer endpoints (https://api.cloudtalk.io/v1/dialer/*) authenticate
with the same HTTP Basic credentials as the rest of the API — your API
Access Key ID as the username and API Access Key Secret as the password.These pages previously documented a separate per-company x-api-key header,
provisioned on request. That scheme was never deployed: requests carrying an
x-api-key header are rejected with 401. If you could not authenticate
against the Dialer endpoints before, use your standard key pair.Rate limiting on the Dialer surface is also per company, not per API
key — a dedicated 60 requests-per-minute budget, separate from the core
API’s quota.Note the Dialer is a newly released product surface: depending on your
account’s rollout stage, campaigns managed through this API may not yet be
visible in your CloudTalk Dashboard.A new home for the CloudTalk API docs
These docs run on a new platform. The API reference is generated from our OpenAPI specification, so what you read here is what the API serves.What you can do now:- Send requests from the page. Every endpoint has a playground. Fill in your own API keys and see the real response.
- Point your AI tools at the docs. There is an MCP server at
https://developers.cloudtalk.io/mcpthat lets an assistant search these pages directly. Every page also has a Copy page menu that hands the content to ChatGPT, Claude, Cursor, VS Code or Perplexity. - Search across every page, and a dark mode that respects your system setting.