Skip to main content

2026-10-23: Auto-recharge can buy the Bronze pack

Update agency auto-recharge now takes BRONZE as its planCode, as it takes SILVER, GOLD and DIAMOND. Bronze is on self-serve price lists only, so a plan your price list does not include answers PLAN_NOT_FOUND. Auto-recharge is still available on paid plans only (FREE_PLAN_NO_AUTO_RECHARGE). See Balances, spend and sub-account wallets.

2026-10-23: TFU Live agents book on Calendly calendars

Look up TFU Live resources for calendars now also lists the calendars of the Calendly account the sub-account has connected, with source calendly and ids such as calendly:<event type uuid>, and Find slots and Cancel appointment steps take them. Calendly books through its API only on a paid Calendly plan. A booking sends the location the event type asks for and answers its required phone question from the contact on file; an event type that needs any other answer from the caller is refused on the call, and nothing is booked. As on Cal.com, a workflow that books on Calendly needs a Collect information step that saves the caller’s email (below). See Build a TFU Live agent.

2026-10-23: Cal.com bookings need a Collect information step for the email

A TFU Live workflow that books on a Cal.com calendar now needs a Collect information step before its Book appointment, with a required item that saves the caller’s email to contact.email. A save refuses a new or changed workflow without one (invalid_workflow, with stepId pointing at the Book appointment). The step reuses an email already on the contact without asking. The agent no longer asks for the email while booking: Find slots asks only for the time, and for the caller’s timezone when times are offered in it and none is on file, as on GoHighLevel and native calendars. Its contact_email argument is gone. A workflow saved before this change is not refused until it is changed, but on a call where the contact has no email it now books nothing, so add the step to it. Cal.com bookings now also carry the contact’s phone number, when it is in international form, as the attendee’s phone. See Build a TFU Live agent.

2026-10-22: Campaigns can share a number, and each number has one answering campaign

Campaigns of your agency can now share a number, in any of its sub-accounts. They must all be TFU Live agents, or all Standard agents. A refused number answers 409, and NumberOnAnotherCampaign is no longer sent. It is replaced by one of these:
  • NumberInAnotherAgency: another agency uses the number.
  • NumberOnTfuLiveCampaign: a TFU Live agent uses it, and this is a Standard agent.
  • NumberOnNonTfuLiveCampaign: a Standard agent uses it, and this is a TFU Live agent.
  • NumberRegisteredToOneBusiness: it is registered to another business in the Trust Center.
  • NumberCannotBeShared: a Standard agent on a different voice account uses it.
Each number has at most one campaign that answers its incoming calls, across your agency. The dashboard shows it as Answers incoming calls. On numbers outside TFU Live, a lead you called now reaches the campaign that called them, in any sub-account using the number. It now goes before the number’s Inbound receptionist and its answering campaign. If the campaign that called them is paused, the answering campaign of its sub-account takes the call. If that one can’t take calls either, the call is refused. A lead you called never goes to another sub-account. A caller you did not call reaches only the number’s answering campaign, on every number. While that campaign is paused, or when no campaign answers the number, the call is refused. No other campaign on the number, and no Inbound receptionist on another number, takes it. List phone numbers now returns answeredBy on every number, not only on TFU Live numbers. campaigns lists that campaign first, then the rest, oldest first. Each campaign has canAnswer, true for a TFU Live campaign or an Inbound agent. The new answeringNow is the campaign in answeredBy while it can take calls. Otherwise it is null. Send locationId and engine, and campaignId for a campaign you edit, and each number’s unavailableReason says why that campaign can’t use it. It is null when it can. A campaign you add a number to answers it if nobody else does. It must be able to answer calls. If another campaign answers the number, that campaign keeps it. To take it over, list the number in answerCalls. Assign phone number, Move phone number and Update campaign now take answerCalls. A listed number that is not on the campaign after the save gets 400 AnswerCallsNotOnCampaign. A campaign that can’t answer calls gets 422 CampaignCannotAnswerCalls. When the answering campaign leaves the number, the next campaign on it that can answer calls takes over, oldest first. See Phone numbers and caller ID.

2026-10-22: TFU Live agents book on Cal.com calendars

Look up TFU Live resources for calendars now also lists the calendars of the Cal.com account the sub-account has connected, on GoHighLevel and native sub-accounts alike, with source calcom and ids such as calcom:4711. sources reports each system on its own, so one that cannot be read is unavailable while the rest are listed. Find slots and Cancel appointment steps take those ids. A save refuses, with invalid_workflow, a workflow whose calendars come from more than one system. A calendar the sub-account’s CRM cannot book on, such as a GoHighLevel calendar on a native sub-account (going live refuses it with calendar_not_on_crm), is left out of that count, so such calendars can be replaced one at a time. Cal.com needs the caller’s email to book: when the contact has none, the agent asks for it on the call and saves it to the contact. An email already on the contact is always used and never replaced. Calendly calendars are not listed yet, and a save refuses one. See Build a TFU Live agent.

2026-10-21: Connecting Cal.com or Calendly checks the key first

Connect a calendar system now uses a pasted Cal.com key or Calendly token for one read in that product before it saves anything. A key the product refuses answers 400 with CalendarKeyRefused, where it used to be saved and fail at the first booking. A product that cannot be reached answers 503 with CalendarKeyCheckUnavailable. Either way nothing is saved, and a connection already there stays as it was. See Calendars.

2026-10-20: Date filters can follow your time zone

List calls and List contacts now take tz, an IANA zone such as America/Los_Angeles. With it, a plain YYYY-MM-DD in dateFromUTC or dateToUTC covers that day from midnight to midnight in that zone, instead of the UTC day. Timestamps are read exactly as before. A request without tz, or with a zone the platform does not know, reads plain dates as UTC days, as it always has. The window on the response shows the instants the filter used. See Dates and times.

2026-10-17: Set up a TFU Live rep’s campaign by API

Get campaign and Update campaign now work for a TFU Live rep, with its tfu_live: id and locationId, so API keys and MCP clients can set its start and stop tags, its calling hours and switches, its cadence of calls and texts, and the number its texts come from, through the same checks as every campaign. Start tags also give the rep a tag start when it has none. What the rep sets itself is refused with TFU_LIVE_CAMPAIGN_SETTING_ELSEWHERE, and details.use names the operation to use instead: its workflows, its numbers, Go live and Pause, its master family and its test links. Changing needs campaigns:write and agents:write. Every campaign also takes smsFromNumber, the number its texts come from. See Set up a TFU Live rep’s campaign.

2026-10-17: After-call steps can save the caller’s name

The post-call AI now saves the name the caller gave for themselves on the call with its summary. After-call steps read it as call.caller_first_name and call.caller_last_name, in text, in a Condition or in a Webhook with response field. Both are empty when the caller gave no name; they never come from the contact on file. A Set field whose value names a call fact that comes back empty is now skipped instead of clearing the field, so contact.firstName set to {{call.caller_first_name}} keeps the contact’s name when the caller gave none. See TFU Live workflows.

2026-10-17: Take a TFU Live rep live, or pause it, by API

API keys and MCP clients can now switch a TFU Live rep’s campaign on and off the way the dashboard’s Go live and pause buttons do, with Go live with TFU Live agent and Pause TFU Live agent. Going live is judged by the same go-live check: while it lists anything, the call is refused with TFU_LIVE_GO_LIVE_BLOCKED and every blocker, and the campaign stays paused. Going live adds no start rule or tag; an outbound rep with start tags begins calling matching contacts once live. Both need campaigns:write and agents:write, and asking for what already is changes nothing. Check go-live readiness now also takes campaignId. See Take a TFU Live rep live, or pause it.

2026-10-17: A retest clears what an earlier test booked or stopped

Before a campaign test runs again on a contact that only test runs have reached, the upcoming appointments its test calls booked are cancelled, without the calendar’s notifications, and an opt-out it gave on a test call is lifted. An opt-out given any other way, or a contact a real call has reached, is left as it is. A booking made by a test call now starts the confirmation campaign as a test run, so it no longer counts as a real lead. See TFU Live authoring. A campaign test runs for real, so a test that booked, parked or passed on its contact used to leave tags that refused every later test of that number. A share link now takes those tags off when only test runs have reached the contact, and starts the run. A contact that a real run already finished with is refused with its own message, CampaignContactFinished. The kill switch, Do Not Disturb, opt-out tags and the client’s own stop tags still refuse as before. A start that is refused no longer counts against the link’s start limits. See TFU Live authoring.

2026-10-16: Buy or connect a TFU Live rep’s number by API

API keys and MCP clients can now give a TFU Live rep its phone number the way the dashboard’s Get a number dialog does: connect a number the agency already has for free, or search, quote and buy one. The purchase is the payer’s yes to the quoted price, needs a billing card on file, and charges the agency wallet once. Reads need agents:read; quoting, buying and connecting need agents:write. Assign phone number and the campaign number move now also work for a TFU Live campaign, with a key that has agents:write as well. See Give a TFU Live rep a phone number.

2026-10-15: MCP browser sign-in guidance

The MCP connection guide is readable before authentication. Discovery metadata links to the guide, and the initial authentication challenge tells clients to start browser sign-in and resume after approval. Existing API keys and OAuth permissions are unchanged. See Connect an AI assistant.

2026-10-15: TFU Live saves through the API take effect at once

Every save through the API or MCP now sets the saved version up straight away: an edit, a restore, or a workflow change. A test link opened after a save uses that version without a separate Prepare call. A draft that still has setup items is set up for test calls only. If a save cannot be set up, its response carries setupError. A test link opened while setup is still running now says the agent is still being set up, instead of a general failure. See TFU Live authoring. The MCP handoff gives you the ready rep’s browser test link before phone setup. Scoped clients can request a GoHighLevel install link and check its status through TFU Live authoring. You still approve installation in GHL and choose where the rep should move.

TFU Live customer guides

TFU Live now groups the visible builder controls into focused guides, with a simple diagram of conversation, Brain and actions. Developer contracts keep their existing URLs under API Reference. The updated guides are available in the customer documentation.

TFU Live manual saves and explicit workflow sync (local development)

Saving your own instructions does not invoke AI or charge for generation. Sync workflows is an explicit action when you want the saved instructions reflected in workflows. Review and test the result before using it with leads. See Instructions and the authoring contract. This change has not been deployed.

TFU Live agency-owner dashboard authoring (local development)

Agency owners can create and edit TFU Live agents in their own sub-accounts using manual configuration or typed and spoken Build with AI. New agents receive a paused campaign. Roleplay and test flows retain resource readiness checks; project users and cross-agency access remain blocked. API keys continue to use scoped public operations. This change is local and has not been deployed. See Build a TFU Live agent.

TFU Live customer authoring (local development)

The local development API adds tenant-scoped TFU Live creation, reads, revision-checked edits, explicit preparation and capability/resource discovery through dedicated TFU Live operations and TFU Live authoring. See Build a TFU Live agent. This change has not been deployed to the production API. Use this page to track public API changes that affect integrations.

October 2026

  • Every error has a code in error and a sentence in message (local development). All endpoints now answer a failure with the same envelope: error is a stable code to branch on, message is one plain sentence for a person, and the optional errors, details and ref say which fields to fix, carry data for your code, and give support a reference. Native Calendar, Native Contacts, Contacts, TFU Live and the power dialer operations under Campaigns used to put a sentence in error; they now put a code there and the sentence in message. Contacts and the power dialer no longer send a separate code field; read the code from error. Existing codes keep their spelling, and fields such as requiredScope are still sent. message is English for API keys and OAuth apps; its wording can change, so never parse it. See Conventions.
  • Validation refusals list their fields in errors (local development). A VALIDATION refusal from Agents, Calls, Campaigns or Contacts used to list the fields in details, as [{ field, message }]. It now lists them in errors, as [{ field, code, message }], like every other refusal: code is stable and message is for a person. details is now an object of data for your code, or absent. When a field must be one of a set, Agents and Campaigns put the choices in details.allowed, by field. List calls still sends details as [{ field }], without message. See Conventions.
  • Native Calendar and Native Contacts list refused fields as objects (local development). Their errors used to be a list of sentences. Each entry is now { field, code, message }, as on every other refusal. errors[].code is a specific code for the rule that was broken, such as ContactEmailInvalid, ContactFieldValueNotNumber, TimezoneNameInvalid or CalendarLengthInvalid, not only the general Field... codes, and field names the field (customFields.<key> for a custom field value). A refusal’s message says what happened, such as a phone number another contact already has or a field a campaign starts on, while error (and code on Native Contacts) keeps the status code it had, such as Conflict or BadRequest.
  • Calls answers a code in error (local development). The call recording downloads used to put a sentence in error, such as Call not found. They now send a code, such as CallNotFound, RecordingUnavailable, CombinedRecordingUnavailable or TransferRecordingUnavailable, and the sentence is in message. Get call still answers NotFound for a call that is not there or not yours.
  • A role refusal no longer names the role (local development). A Forbidden from a role check used to carry requiredRole. It is no longer sent. requiredScope on InsufficientScope is unchanged.
  • message is in the reader’s language (local development). Every refusal’s message, and each message in errors, is written in the language of the person reading it: the dashboard asks for the language the person chose. Requests made with an API key or an OAuth app, MCP included, always get English, whatever Accept-Language they send. error, code and every other field stay the same in every language. See Conventions.
  • TFU Live has a focused customer guide. Start with TFU Live to learn where to edit Instructions, Conversation Style, Workflows, Chat and Call handling, then test and go live. Developer contracts remain in API Reference.
  • A TFU Live campaign’s browser test link uses a contact already on the number (local development). For scope: "campaign", the web link from Issue TFU Live test links no longer refuses a number that belongs to a contact a test link did not make. It finds the contact by the number the tester enters, as the phone link does, and runs the campaign for it. In GoHighLevel the contact takes the name the tester enters. A contact the web link creates in GoHighLevel still carries the source TFU AI Test Link. The tester’s page now says why a run did not start for their contact: Do Not Disturb, calling stopped, or a number that changed in the CRM. See Build a TFU Live agent.
  • TFU Live campaign test links run in the browser (local development). For scope: "campaign", Issue TFU Live test links no longer answers web.available: false with the blocker WEB_CAMPAIGN_PHONE_ONLY. The web link now runs the real campaign for the contact made from the name and phone the tester enters, in their browser, with no calling line needed, and it is charged nothing, as the phone test run is. The web link takes a number only when no contact holds it yet, or when a test link’s browser run made the contact on it. That contact stays usable for 30 days after its last browser run through a test link, and each run that uses it starts the 30 days again. Any other number is refused and its contact is left untouched. A contact the web link creates in GoHighLevel carries the source TFU AI Test Link. The response carries web in two cases: with the blocker WEB_CRM_UNSUPPORTED when the sub-account’s CRM is not one TFU Live runs on, and with the new blocker WEB_LINK_PHONE_ONLY when a reused link was issued before this change. That link’s phone side works as before, and rotate: true issues one that runs in the browser. See Build a TFU Live agent.
  • Transcript search (local development). The API and MCP add literal words and phrases with AND/OR, bounded excerpts and signed continuation. Search covers saved transcripts, including older calls. Counts are matching calls, with optional coverage of how many calls were searchable. See Transcript search.
  • Large MCP tools list a lighter schema. Ten tools whose bodies nest several objects deep, such as patch_agent_builder_agents_by_id and update_agent_campaign, now appear in the tool list with shorter field descriptions, and some list their deepest objects by key name, so every client can read the whole schema. Before this, Codex stripped the descriptions from oversized schemas and showed four of these tools with no body fields at all. Every field is still accepted and checked against the full schema, and search mode still returns the full schemas. The MCP handshake instructions are also shorter, with the rules about confirming writes and untrusted text first. See Large tools.
  • Every MCP write tool is marked destructive. All write tools now carry destructiveHint: true, creates included, so a client that honours the hint asks before each one, including placing a call, running the dialler and buying a number. Reads are unchanged. See Reads and writes.
  • Search mode for MCP clients that limit tools. Add mode=search to the MCP URL and tools/list returns search_tools, list_tools, call_read_tool and call_write_tool instead of every tool. Everything stays reachable and listable, with the same checks. See Search mode.
  • The MCP tool list matches your role. tools/list now leaves out tools your role cannot use, as well as those your credential lacks the scope for. A role without agency admin rights, such as a sales rep (rep), sees the call, contact, analytics and Power Dialer tools instead of every customer tool. See Tool visibility.
  • Two MCP tools have shorter names, so AI clients that limit tool name length show them: post_agent_builder_phone_number_campaign_move and delete_agent_builder_phone_number_campaign, for moving and unassigning a campaign’s phone number. Their operation IDs in this reference are unchanged.

September 2026

  • A refused TFU Live campaign says so. When Create TFU Live agent saves the agent but its campaign is refused, the response keeps the refusal’s status and message with the code TFU_LIVE_CAMPAIGN_REFUSED, instead of reading as an unconfirmed campaign to retry. Like the rest of TFU Live authoring, this is a local development contract. See TFU Live authoring.
  • Analytics by lead age. Get analytics by lead age returns the analytics summary split into fresh leads (sent within 5 minutes of being created in the CRM), warm (under 3 days) and dbr (3 days or more), plus unknown for leads whose times cannot place them, with unknownReasons saying why. The segments add up to the summary. See By lead age.
  • A contact now says how old the lead was when it arrived. List contacts and Get contact return leadGeneratedAtInCrm, when the sub-account’s CRM created the lead, with leadGeneratedAtInCrmPrecision saying whether that is an exact time or only a day, and leadReceivedByTfuAi, the exact moment the lead reached the platform. dateAdded is unchanged. See How old a lead was when it arrived.
  • A TFU Live agent’s orb has its own colours. Set TFU Live agent orb colour picks one of four kinds: default (it follows the voice), curated, one of 17 preset pairs (each a published palette’s first real colour as the main colour and the next colour that differs from it as the glow), or two custom colours (the orb’s own colour, then its glow). Every agent read, starting with Get TFU Live agent, now returns orb, and {"style": "default"} when none was chosen. Choosing a colour takes no ifVersion and saves no revision. Like the rest of TFU Live authoring, this is a local development contract. The orb colour explains each kind.
  • A campaign workflow can post one outcome to your system. send-webhook is now a published node type on Add actions to an outcome and Replace the entire workflow, with its receiver in config.url. The node already worked in the builder; the reference left it out, and Add actions to an outcome refused it. It sends the signed call_ended event only when its outcome fires. Outcome webhooks covers the body, signing and retries.
  • Contacts and calendars without a CRM are in the API. Twenty-one operations under /api/native-contacts and nineteen under /api/native-calendar cover what a sub-account that keeps its contacts here needs: create, read, update and delete contacts, define the custom fields your prompts read, import a CSV, enrol contacts into a campaign, set do-not-call, and read or rotate the lead link; then the calendars themselves, their free slots, and booking, rescheduling, confirming and cancelling on them. Contacts use the contacts:read and contacts:write scopes an existing key already carries; calendars use projects:read and projects:write. Contacts without a CRM and Calendars and booking are the guides.
  • Native cadences can send webhooks. api-event actions on Update campaign schedule (not published on this branch; the cadence is edited under calling.cadence on Update campaign) send a signed HTTPS event with contact and cadence details plus static custom JSON. Event-only steps do not place a call. Destinations and data read back on Get campaign schedule (not published on this branch; read calling.cadence on Get campaign). Send webhooks covers the fields and delivery behaviour. The step is called Send webhook in the builder; api-event remains its stored type, so nothing you send changes.
  • An agent can wait for hello. config.llm on Update agent takes startingSpeaker (agent greets first, user waits for the person) and beginAfterUserSilenceMs (how long a waiting agent gives a quiet person before it speaks, 1000 to 15000). Both read back on Get agent. Who speaks first explains the choice.
  • Billing changes are in the API. Ten operations under /api/billing switch auto-recharge on or off and pick its plan, buy a plan, turn a sub-account’s rebilling on or off, link or unlink its Stripe customer, set its auto-recharge and Credit Guard, and charge its client’s card. Anything that charges a card needs the new billing:charge scope and an Idempotency-Key, and is not available over MCP. Every sub-account change needs rebilling on your plan and Stripe Connect connected. Set up rebilling for a sub-account walks through it.
  • Billing is in the API, read-only. Six endpoints under /api/billing answer where your agency stands: a summary of balance, auto-recharge, subscription, this month’s spend and what needs attention; one history of everything that moved your wallet; usage by sub-account or by day; invoices and receipts; and every sub-account’s wallet. They need the billing:read scope and an agency admin’s key, and cover only the agency the key belongs to. Balances, spend and sub-account wallets explains every figure.
  • A cadence step can say its own words. A step’s call now also takes openingMessage, screeningPurpose, voicemail and voicemailMessage, so each touch can open differently, give its own reason when a phone screens the call, and leave its own voicemail. Leave one out and the agent’s own opening and purpose, and the campaign’s voicemail, apply. voicemail: true without a voicemailMessage returns 422. Cadences, steps and the queue has the detail.
  • An agent’s config.voicemail is documented. It reports the agent’s own voicemail handling: enabled, message and detectionPrompt. timeoutMs stays in the shape and is null, because no detection timeout is reported any more. It is read-only, and separate from the campaign’s voicemail below. Get agent shows it.
  • One voicemail switch per campaign. config.voicemail, config.voicemailMessage and the optional config.voicemailDetectionPrompt leave one voicemail per cadence touch, on its last dial: the second dial when the touch double dials. Callbacks and inbound calls never leave one. dropVoicemailOnDoubleDial, doubleDialVoicemailMessage and doubleDialVoicemailDetectionPrompt are deprecated and still read while the new keys are absent. What a campaign is has the detail.
  • Your agency’s voice library is in the API. Five operations joined Voices: List added voices, Search for a voice to add, Add a voice, Preview removing a voice and Remove a voice. Search, add and remove need the new voices:write scope. List voices now also returns your own and shared voices, each row with a source of own, shared or curated. An add can be refused with 409 and VOICE_ALLOWANCE, VOICE_CAPACITY or VOICE_ALREADY_SHARED. Voices has the detail.
  • Trigger tags are stored in lowercase. On GoHighLevel sub-accounts activeTags and inactiveTags are saved in lowercase, the way GoHighLevel sends them, and tags that differ only in case are merged. A trigger saved as Hot Lead now fires on the hot lead GoHighLevel sends. Who gets called has the detail.
  • A master agent can be split tested. Converting a master agent campaign no longer returns 409. Run the split from the parent, and every linked project applies the weights to its own leads. Converting or adding a variant through a child returns 409. Agent split tests has the detail.
  • A split test variant can sit out of the cycle. Update split weights now accepts weight: 0 for any variant but the primary. The variant comes back with splitDraft: true, takes no calls, and stays testable. While it is out, one live variant covers the calls owed to its leads, the same one every call, and including it again hands those leads straight back. Covering calls count in the campaign’s totals but not in any variant’s own numbers. Agent split tests has the detail.
  • A campaign’s name follows its agent’s name. A live campaign’s name is kept in step with its agent’s, so a name sent on Update campaign or Update project campaign does not last. Rename the agent with Update agent to rename both. A campaign running an active split test, or whose agent’s name another campaign in the project already has, keeps its own. What a campaign is has the detail.
  • Corrected: a split test does not return startedAt, so the guides no longer mention it. The List voices filters never hid your own or shared voices, though the reference said they did. Preview removing a voice never returns 404. A cadence step’s call ignores keys it does not know rather than refusing them. An excluded split variant does not keep its in-flight leads; a live variant covers them, as described above.
  • Project selectors require Location IDs. Use locationId for one project or locationIds for several. projectName and projects are display labels; name-only selections return 400. Conventions explains the identity fields.
  • Get voice answers only for voices in your voice list. That’s the catalogue, your own voices and shared ones. Any other id is a 404. A voice from your list that is not in the catalogue comes back with isCustom: true and its gender, accent and age, with voiceName empty. Voices has the detail.
  • Billing access is separate from subscription status. Read AccessSubscriptionRequired rather than inferring access from subscription state: an agency can have software access without one. Agency boundaries, user roles and API key scopes still apply. Existing billing records and provider subscriptions remain unchanged. Conventions explains billing access errors.
  • The docs now cover every published concept, and the two tabs link both ways. The Documentation sidebar mirrors the API reference groups one to one, with a guide for each module that had none: Call records, Contacts and leads, Metrics and how they are calculated, Cadences, steps and the queue, Campaign workflows, Phone numbers and caller ID, Voices, GoHighLevel resources, Agencies, projects and sub-accounts, Agent split tests and Master Agent. Every reference group opens with a landing page that links its guide, and every guide ends with the endpoints behind it. A Glossary defines every field and enum value, and the docs are checked on every build for a broken link, an orphaned page, or an enum value the docs do not mention.
  • New: Quickstart, Test your agent, Clone an agent, Run a split test and Backfill existing leads.
  • Conventions now carries the numbers. Rate limits with their defaults and the RateLimit-* headers, every error code that appears in published responses, which id to use for what, the four operations that accept Idempotency-Key, the concurrency tokens, and the legacy aliases.
  • POST /api/agent-builder/agents/{id}/web-version/session is documented. The reference now describes the browser test session: the accessToken, callId and agentName it returns, the billing gates that answer 409, and the 502 VoiceProviderUnavailable when the voice provider refuses to start. No behaviour changed.
  • Corrected: outcomes follow capabilities, not the campaign type. The after-the-call guide said which outcomes exist depends on the agent’s type. It depends on the installed capabilities, as the agent-types guide already said. Corrected: the introduction claimed billing, agency settings, access subscriptions and events were published modules. They are not.
  • Moved: the skills-for-voice-ai concept page is merged into Skills and tools. The old URL redirects.
  • The deprecated project-scoped campaign workflow paths left this reference. /api/agent-builder/projects/{locationId}/campaigns/{campaignId}/workflow and its digest and node paths are no longer listed here, so the reference shows only the agent-scoped paths to move to. They still work and still send a Deprecation header. The whole-workflow DELETE is the exception: it needs a dashboard session and refuses API keys, and it never appeared here.
  • Corrected: Idempotency-Key on Add split variant. The operation described the header but never listed it, so a generated client had no way to send the one thing that makes the call replay-safe. The header is now a documented parameter. The request body no longer advertises an idempotencyKey field: the route strips it and reads the header only, so sending it in the body never guarded a retry. No behaviour changed.

August 2026

  • The outreach campaign type is now labelled “Outbound”. Only the display name changed: GET /api/agent-builder/agent-types returns label: "Outbound" where it used to return "Outreach", and the same wording appears in the validation message you get back when you omit capabilityIds. The type id is still outreach everywhere (in agentType, in stored campaigns and in the legacy system mapping), so nothing you send or store needs to change. If you display our label rather than your own, your UI will follow.
  • The MCP action endpoint advertises every published operation. Placing calls, buying or releasing numbers, running the power dialler, deleting projects, clearing a do-not-call flag and enrolling leads are all reachable as tools with a broad enough credential: the MCP surface is exactly what this reference publishes, filtered only by the credential’s scopes. If your client auto-approves tools, note that destructiveHint does not flag these: placing a call, running the dialler, buying a number and creating an agent are all creates, and creates are annotated destructiveHint: false. Scope the credential accordingly. See MCP action tools.
  • Contacts can be marked as your own test leads. POST /api/contacts/internal marks a contact internal and DELETE /api/contacts/internal clears the mark; both take locationId with either contactId or phoneNumber, and report how many records the identity resolved to. List contacts takes a matching internal filter: all (the default) lists them with isInternal on each row, only narrows to them, exclude hides them. The default lists them.
  • tagged is now a disposition you can filter on. It was previously only a breakdown figure, so a tagged count on the dashboard could not be turned into a list of which contacts. It reads as one option rather than one per tag, since which tag was applied is the breakdown’s question and the tags differ per client. tagged never masks another outcome: tagged plus booked reads appointment.
  • POST /api/power-dialer/move is now published. The route existed but was missing from this reference. It moves waiting leads to a different step of their schedule and recomputes the next call time from the target step in each lead’s own timezone. Only waiting leads move; one that is mid-dial, pinned to a callback, on an appointment-reminder schedule, or whose schedule has no such step comes back in skipped with a reason instead of failing the request.
  • Corrected: the scope table described several scopes wrongly. Nothing changed in the API; the Authentication table was wrong, and a key scoped from it could come back 403 with no clue why. The worst of it: outcome prompts are covered by campaigns:read and campaigns:write, not agents:read and agents:write as the table claimed. Also corrected: calls:read and contacts:read no longer claim to cover CSV exports, and calls:read no longer claims execution history, none of which this reference publishes; contacts:write now mentions marking a contact as a test lead and names the do-not-call flag rather than an internal “kill switch” spelling; ghl:read no longer claims pipelines and workflows, which it does not reach; power_dialer:write names moving leads between steps rather than a “requeue” that does not exist; projects:write no longer claims to create projects, which it cannot; and the agents:* rows now mention the agent’s skills and tools, which they do cover and did not say.
  • webhooks:read and webhooks:write are documented. Both scopes were already issuable and enforced; the Authentication scope table omitted them.

July 2026

  • Breaking: agentType on the Agents module is now a campaign type, not a combined template id. Create agent, the agentType filter on List agents, and the agent responses accept and return outreach, inbound, or appointment. What the agent can do moved to a separate field, capabilityIds on create, which takes one or more of appointment_booking, live_transfer, and add_tag. The seven combined ids listed under “Agent types are now a fixed, validated set” below are no longer accepted as agentType; sending one now returns a 400 listing the three valid types. Integrations that send a combined id as system, with no agentType, are unaffected and keep working. See Campaign types and capabilities for the mapping from each old id to its type and capability set.
  • A campaign’s installed capabilities are now readable. Agent responses carry capabilities on the campaign facet, so you can tell what an agent can do without inferring it from a template id. Capabilities also gate outcomes: a call can only end in TRANSFER when Live Transfer is installed, and in SUCCESS or RESCHEDULE when Appointment Booking is.
  • GET /api/agent-builder/agent-types returns three entries, not seven. Outreach and Inbound each carry a templates array listing the starting capabilities you can pass as capabilityIds. Reminder resolves to a single fixed template.
  • The Campaigns and V2 Projects modules still speak the seven template ids. Their agentType is unchanged and still validated against the original set. It names the template a campaign was cloned from, not what the agent can do, and on an agent created with several capabilities it reflects only the first. These modules will be migrated separately; until then, treat agentType on the Agents module and agentType on Campaigns/Projects as different fields that happen to share a name.
  • Appointment Booking is now a documented skill type. It was already installable and has been all along, but the reference omitted it from the skill type set and from GET /api/agent-builder/agents/{id}/skills/library. Both now list it. No behaviour changed; the documentation was wrong.
  • Breaking: call records answer with the canonical field names. A call’s identifier is id and its agent type is agentType. No other spelling of either field is returned. The list payload also no longer carries cached, queryTime, and generatedAt. Those described the server’s own caching, not your data, and were never something to build on.
  • Breaking: execution-history responses nest under data and use camelCase. The run summary, node history, runs list, timeline and graph endpoints now return { "success": true, "data": { … } } with the same camelCase names the rest of the API uses.
  • Campaign workflows can now be addressed by the agent that owns them. A workflow is reachable at /api/agent-builder/agents/{id}/campaign/workflow, alongside endpoints to read a digest, append an action to an outcome, switch an outcome, and delete a node. The project-scoped equivalents under /api/agent-builder/projects/{locationId}/campaigns/{campaignId}/… still work and are unchanged, but they are deprecated: they now carry a Deprecation response header (RFC 9745). They will be removed in a future release, so move to the agent-scoped paths.
  • Fixed: fetching a call by its platform call id. GET /api/call-history/by-call-id/{callId} returned 404 for every call, including ones that exist. It now returns the call.
  • Fixed: excludeReviewed=false no longer excludes. The value was coerced in a way that read any present value as true, so passing false did the opposite of what it says. excludeReviewed now takes exactly true or false, and any other value is a validation error rather than a guess. disposition is also accepted as a repeated parameter again, not only as a comma-separated string.
  • A campaign’s agentType and dialing are validated wherever a campaign is written. The project-scoped campaign routes accepted any string for either field and stored it, after which the campaign read back with agentType: null and no way to tell from the write that anything was wrong. Both fields are now checked against the published sets on those routes too, and an unknown value is rejected with a 400 that lists the valid ones.
  • Agent types are now a fixed, validated set. agentType accepts only appointment_booking, appointment_confirmation, live_transfer, live_transfer_and_tag, live_transfer_and_booking, add_tag, and inbound_receptionist. An unrecognised value is now rejected up front as a validation error listing the valid types, instead of failing later as a not-found. The same set applies to the agentType filter on List agents. The agent type is also always present on an agent response now. It is null when none can be resolved, where it was previously left out of the object.
  • Breaking: the confirmation and reactivation dispatch modes are retired. Neither can be set on a campaign any more: creating or updating with "dialing": "reactivation" (or "confirmation") is rejected with a 400, and the writable modes are now s2l, pd, and s2l_pd. Campaigns created before this still work: they keep their stored mode, keep dialling exactly as before, and still report "dialing": "reactivation" on a read, which is why that value remains in the response schema. What they cannot do is have their dispatch mode re-sent: an update that includes the retired value is refused, so change the mode to a supported one in the same request if you need to edit such a campaign. confirmation is no longer accepted on a write. A read still returns it for a Reminder campaign, and List agents reports the same campaign as pd.
  • Breaking: a campaign’s dialing no longer accepts null. Sending "dialing": null when creating or updating a campaign is now rejected with a 400. Omit the field to leave a campaign’s current dispatch mode alone, or, on create, to take the default for the agent type (outbound types default to pd). There is no “clear the dispatch mode” operation, because a campaign without one is inert: it never dials, and it also hides the controls you would use to set it, so it cannot be repaired from the dashboard. A campaign storing null is given its correct mode on its next write.
  • Breaking: three campaign-workflow action types have been removed. post-webhook, enroll-in-campaign, and requeue-pd are no longer valid values for a workflow node type, and are rejected on write. The supported actions are add-tag, remove-tag, set-field, move-stage, add-note, send-sms, send-email, and book-appointment, plus the if and switch branch nodes. Re-dialling a contact who did not answer is handled by the engine itself and never needed a node. The webhook node’s url, payload, headers, auth, and retry config fields are gone with it.
  • Workflow configs are rejected if they contain an unreplaced placeholder. A node config carrying a stand-in value (REPLACE-WITH-…, CHANGE-ME, or a URL on a reserved documentation host such as example.com) now fails to save. An empty config is still accepted: an unfinished draft is fine, a value that only looks finished is not.
  • Post-call analysis fields are now protected on update. When you update an agent, platform-managed analysis fields (the standard call outcomes and other system-managed fields) are always retained, even if your request omits them. A partial update that sends only your own custom fields no longer removes the managed ones. Only the custom fields you send are replaced.
  • Added public API documentation for launched customer-facing modules.
  • Added bearer API key authentication guidance.
  • Added standard API response and error conventions.