Deprecation policy
Breaking changes ship as a new major path (/v2) with a migration guide, a deprecation notice on the old path and a sunset date announced here at least 90 days ahead. Additive changes (new fields, new routes, new event types) ship on /v1 without notice and are safe to ignore.
AI disclosure setting
- Each agent has an AI disclosure setting (
ai_disclosurein the draft,compliance.yaml.ai_disclosure.enabled), off by default for new and existing agents. On: the agent says it is a virtual assistant right after the owner’s greeting, as before. Off: the owner’s greeting is said word for word, with no AI sentence added. Changing it is a HIGH-risk change, decided by a named person on Publish and kept in the agent’s change and version history. The recording notice is unchanged: it follows the greeting whenever calls are recorded and is never said when they are not. The truthful answer to “are you a person?” does not depend on the setting.
saved links and client guidance
- Legacy App links retain their query parameters and fragment at the final workspace redirect, including repeated filters. Parameters and anchors explicitly set by a migration take precedence. Sign-in still uses the same-origin return address; a visitor without a workspace still goes to onboarding, without automatic saved-link resumption afterward.
- Supported clients now distinguishes the source-available TypeScript and Python SDKs from package publication. The MCP guide makes the complete bearer header value explicit. No API route, credential boundary or database schema changed.
POST /v1/callsacceptsmode: "prompt": the conversation is onlytask(orsettings.prompt), withfirst_sentence,voiceandlanguagefor this call.agent_idremains the line for number, consent, do-not-contact, calling hours, caps and billing. A prompt is required.agent_version,productionandenvironmentare refused.prompt_onlyis not a field and skips no check. Sessions and calls and TypeScript and Python SDKs.
recovering a stale scenario save
POST /v2/agents/{agent_id}/scenarios/{id}keeps its existing revision check and409 CONFLICTenvelope, addingreason: stale_scenarioand the current draftrevisiononly for a stale save, including a race at the atomic draft write. Other conflicts and missing or foreign pathways disclose no revision. The App asks Keep my changes or Discard changes before recovery.
Sempera voice and engine identities
GET /v1/voicesaccepts Sempera engine aliases inengineorprovider, rejects invalid aliases withreason: invalid_voice_id, and answers catalogue refusals in Sempera’s words.- Spec exports and frozen environment settings seal both speech legs’ engine and model identities, including retained staging snapshots created before this change. Stored agents, voices and version hashes are unchanged. No data migration is required.
- Conversation details, call-log summaries, live-call engines and voice handovers use Sempera identities for locally served speech too. Advanced views are not an exception. Operators retain the original runtime record.
credits
Added
GET /v1/creditsandGET /v1/credits/history: a workspace’s credit balance, what each action costs, and every movement of it (Billing boundaries).left_unscoredon eval runs and quick runs: what a run’s credits did not reach.402 credits_exhausted: building actions (POST /v1/tests,POST /v1/evals/run,POST /v1/evals/quick-run, simulation and Standards runs, knowledge uploads and re-reads, and a call withtest) now spend credits, and refuse withneeded,balance,debtand, where buying can work,buy_pathwhen the balance cannot cover them.POST /v1/callswithouttestandPOST /v1/sessionsare unchanged and spend none.
sign-in for AI assistants
Added
- OAuth 2.1 sign-in on
POST /v1/mcp: discovery documents, client registration, authorization code with PKCE, exact redirects, audience binding, rotating refresh tokens and an owner consent page in the App. phone:read,phone:callandphone:textscopes. A credential holding only these can connect toPOST /v1/mcp.GET /v1/client-grants,POST /v1/client-grants/restandDELETE /v1/client-grants/{grant_id}: one list of connected clients, a scoped REST credential for clients that cannot do OAuth, and revocation of one client at a time.
Changed
- A
401onPOST /v1/mcpnow carriesWWW-Authenticatewith the protected resource metadata address. Its body is unchanged, and bearer keys work as before.
official SDKs
Added
- TypeScript (
@sempera/sdk) and Python (sempera-sdk) client libraries, generated fromopenapi.jsonwith one method per operation, typed errors, retries that honorRetry-After, automatic idempotency keys, cursor paging and webhook signature verification (TypeScript and Python SDKs). They are not published to npm or PyPI yet. No route changed.
metric alerts
Added
PATCH /v1/alerts/{id}takes an optionalexpected_updated_at, theupdated_atthe caller read, including when enabling or disabling a rule. The check and save are one step; if another owner saved first, the answer is409 conflictwithreason: stale_alertand theupdated_atnow on file, and nothing is written, audited or cleared. A value that is not a time with a zone is400 validation_failedonexpected_updated_at. A rule of another workspace, or one deleted meanwhile, is404 not_foundand names noupdated_at. Without the field a save behaves as before (Metric alerts).
docs and contract alignment
Added
openapi.jsonnow describes what the docs already said: theruntime_identityblock on a session, anEvalRunschema forGET /v1/evals/runsandGET /v1/evals/runs/{run_id}, theeval_idandcall_idsfields ofPOST /v1/evals/run, and the seven tags (MCP, Models, Prompts, Scenarios, Widgets, Citation schemas, Developer settings) that routes used without a description.- Errors and limits lists the stable
reasonvalues a refusal can carry, includinginvalid_voice_id(a value that looks like a Sempera voice id and is not one), and Sessions and calls documents the fields ofruntime_identity. - Owner features in the App says which staging and analytics features are App-only (served under
/api, not/v1).
what ran a session
Added
- Evals:
GET /v1/evals/runsandGET /v1/evals/runs/{run_id}read the evaluation runsPOST /v1/evals/runnow records, and that call’s answer carriesrun_id(Tests and evals). GET /v1/sessions/{session_id}returnsruntime_identityfor a session recorded from now on: the engine and model that heard, thought and spoke (named by Sempera’s opaque engine names), whether any leg fell back, whether Sempera’s own servers or a hosted call service ran it, the reply and voice timings, and whether the session was a test or a customer’s conversation.verifiedis false when the stored block no longer matches its own integrity checksum. Older sessions returnnull.
voices
Changed
- Every voice a customer reads is named by a Sempera voice id (
voice_then lowercase letters and digits) and neutral fields, never by the speech engine or the engine’s own id for the voice:GET /v1/voices(each row is{id, voice_id, name, display_name, description, language, source, production_eligible, preview_available, available}, plusvisibilityfor a custom voice), the Agent’svoice, a draft’svoice, a session’svoice.voice_id, a persona’s and a call’s voice, and the agent export’sagent.yaml. Theproviderandenginefields ofGET /v1/voicesare removed. A voice of a published Sempera model keeps the name its model lists. - The ids are stable and safe to store, and one voice has one id whichever transport of an engine it is spoken through. A raw voice id from before this change is still accepted wherever a voice is written (a draft, a session, a favorite, an agent file) and keeps meaning the same voice; reads return only Sempera voice ids.
- An id that looks like a Sempera voice id but is not one is refused the same way everywhere:
400withreason: invalid_voice_id(code: invalid_voice_idon the App’s own routes), on a draft, a session, a voice’s detail and preview, and a favorite. - A voice refusal is in Sempera’s words (“That voice is not in the voice list.”) and never repeats the engine or the engine’s id for the voice.
POST /v1/sessions:voiceandspeech_recognitionno longer carryprovider,fallback_providerormodel, except a leg on a published Sempera model, which is still named by its Sempera model id.voice.voice_idis the Sempera voice id.GETandPATCH /v1/agents/{id}/draft: the draft’sfieldsname a speech engine (runtime_tts_provider,runtime_tts_fallback,runtime_stt_providerand a speech selection’sprovider) by a sealed Sempera engine name (engine_then letters and digits), and a voice by its Sempera voice id. APATCHtakes the sealed name or the engine id. The draft summary previews a speech engine by its plain label (“Studio voice”, “Standard recognition”).- The runtime reading names a speech engine and its model by a sealed Sempera name too: the
runtimeblock’s hearing and speaking legs (options[].value,identifierandmodel, theconfiguredandeffectivevalues, the speaking pace’s engine) in theresultof a draftPATCHand onGET /v1/agents/{id}. APATCHtakes the sealed name back, and the engine id still writes. An option’sreason_detailis the same owner sentence as itsreason. - A refused voice reads by the refusal’s code, never its internal sentence:
voice_unknown,engine_unavailable,engine_not_allowed,catalog_unavailable,preview_unavailable, and for a custom voice’s consentconsent_no_workspace,consent_store_unavailableandconsent_missing(asreasonbesidecode: voice_consent_required). - Agent files and GitHub sync:
agents/<slug>/agent.yamlnames each voice by its Sempera voice id (voice: {id, name}) and each speech engine by its sealed name, so nothing the agent’s repository holds names the engine. Importing reads both back to the same runtime values, an older file with raw ids still imports, and the content hash Sempera reports for a file tree is the same in either form. The first sync after this change commits the rewrittenagent.yamlonce.
webhooks
Added
PATCH /v1/webhooks/{id}takes an optionalexpected_updated_at, theupdated_atthe caller read. The endpoint is saved only if it still has thatupdated_at, in the same step as the check, whatever the edit changes (the address, the subscriptions, a pause or a new signing secret); if another owner saved first, the answer is409 conflictwithreason: stale_webhookand theupdated_atnow on file, and nothing is written. A value that is not a time with a zone is400 validation_failedonexpected_updated_at. An endpoint of another workspace, or one deleted meanwhile, is404 not_foundand names noupdated_at. Without the field a save behaves as before (Webhooks).
widgets
Added
- Saving a website widget in the App takes an optional
expected_updated_at, theupdated_atthe owner read. The widget is saved only if it still has thatupdated_at, in the same step as the check; if another owner saved first, the save is refused with409andcode: stale_widget, the answer carries the widget now on file, and nothing is written or audited. A value that is not a time with a zone is400onexpected_updated_at. Without the field a save behaves as before. No/v1route changes:GET /v1/widgetsstill leavesupdated_atnull (Website widgets).
triggers
Added
PATCH /v1/automation/triggers/{id}takes an optionalexpected_updated_at, theupdated_atthe caller read. The trigger is saved only if it still has thatupdated_at, in the same step as the check; if another owner saved first, the answer is409 conflictwithreason: stale_triggerand theupdated_atnow on file, and nothing is written or audited. A value that is not a time with a zone is400 validation_failedonexpected_updated_at. Without the field a save behaves as before (Automations).
documentation
Added
- Three pages: Security and privacy, Billing boundaries and Supported clients. The quick start gains what to have before you start and what the API does not do yet. API overview lists the routes that need no credential and explains the difference between supported and compatibility routes. Agents, Workspaces and organizations and Tools and integrations now name the five supported routes they had left out:
GET /v1/agents/{id}/versions/{version_id},POST /v1/agents/{id}/unarchive,GET /v1/organizations/{organization_id},GET /v1/integrations/{integration_id}andGET /v1/integrations/{integration_id}/actions. No route changed.
Changed
- Examples use the demonstration agent
northwind-barbersin place of a customer’s agent id, and invented ids, digests and timestamps in place of any value from a customer’s record. No route or field changed. - The quick start and Authentication and credentials no longer say the App has no key screen: a workspace owner creates API keys in the App under Settings, API keys, and a workspace’s first key is created there. No route or field changed.
- Webhooks describes provider ingress by category and no longer lists its routes one by one: they are for Sempera’s service providers, not for integrators. Web SDK and Sessions and calls describe the realtime media client by category. No route or field changed.
- Billing boundaries says exactly what the two
/v1/compatbilling reads may write (the workspace’s default billing record on a first read) and nothing more. API overview’s table of routes that need no credential lists all eight.
shipped after that record
Recorded from the canonical branch’s history. Dates are the commits’. Owner features in the App with no API route are in Owner features in the App and Text messages.
Changed
POST /v1/callsfor a number on the workspace’s do-not-contact list still answers501 capability_unavailable, now withreason: do_not_contactand the message “No call was placed: the number is on this workspace’s do-not-contact list.” The refusal is written to the workspace’s audit log with the number’s last four digits only, and the one-call batch is still recorded with that person marked as blocked. It used to answer “outbound call was refused” with noreasonand write no audit row (2026-09-30, Sessions and calls, Errors and limits).channels.phoneon the Agent (GET /v1/agents,GET /v1/agents/{id}, the Agent returned by create, archive and unarchive, and the MCPlist_agentsandget_agenttools) istrueonce a phone number is set up for the agent: a number bound to it, or a line released through its deployments, the same reading as the App’s launch checklist. It was alwaysfalse(2026-09-29, Agents).DELETE /v1/integrations/{id}(archive, or restore with?restore=1) takes an optionalrevisionquery parameter, the revision the caller read; one that is no longer on file is refused with409 conflict,reason: stale_connectionand the revision on file, and nothing is archived or restored. Without it the call behaves as before (2026-09-29, Tools and integrations).- An agent’s
greeting(its opening line) no longer replaces the whole opening. The agent always follows the owner’s words by saying it is a virtual assistant and, when calls are recorded, with the recording notice, in the language of the call; an opening line that already asks the caller a question is not followed by a second one. The compiled opening the compatibility reads return asfirst_sentencecarries both. It used to be the owner’s words alone (2026-09-29). identity_stylesets only how the agent introduces itself:ownedspeaks as part of the front desk, by name, andvirtual-assistantleads with the title. Every agent says it is an AI virtual assistant at the start of every call, and says plainly that it is an AI when a caller asks whether it is a person, whatever this field holds;ownedused to make it answer without saying so. The values are unchanged and an agent that holdsownedkeeps it (2026-09-29).POST /v1/agents/{id}/preview: the first reply of a typed session starts with the agent’s opening (the AI disclosure and, when calls are recorded, the recording notice) in the language the caller wrote in, as a call’s first words do. It used to be a greeting the model composed (2026-09-29, Agents).PATCH /v1/integrations/{id}refuses a save built on an older revision with409 conflict,reason: stale_connectionand the revision on file. It answered400 validation_failedwith a sentence, against the rule in Errors and limits that a stale revision is a409(2026-09-29, Tools and integrations).DELETE /v1/tools/{id}(archive, or restore with?restore=1) takes an optionalrevisionquery parameter, the revision the caller read; one that is no longer on file is refused with409 conflict,reason: stale_tooland the revision on file, and nothing is archived or restored. Without it the call behaves as before (2026-09-29, Tools and integrations).PATCH /v1/tools/{id}refuses a save built on an older revision with409 conflict,reason: stale_tooland the revision on file. It answered400 validation_failedwith a sentence, against the rule in Errors and limits that a stale revision is a409(2026-09-29, Tools and integrations)./v1/knowledge-sourcesfills the fields it documents:content_type,added_atandattachedwere alwaysnull,collectionswasnullon an upload and on a source in no collection, and the list’spdf_supportedwas alwaysnull. An upload now answers what a read of the source answers,rawincluded: an upload’srawis the library row a read answers, where it was the store’s own record (it gains the fields a read carries, such asrevisionand, for text documents,text, and losesmime,extractedandremoved_at); if that read fails after the file is saved, the upload answers201from the saved record, withcollectionsandattachednull.POST .../reparseanswers the state the re-read left, where it always saidreparsing, andDELETEandPOST .../reparseanswer an id the agent’s library does not hold404 not_found, as the read does, where they answered409 conflict; a source already removed, or a library that cannot be read, still answers409 conflictwith the reason (2026-09-30, Knowledge).PATCH /v1/knowledge/{id}/contenttakes an optionalexpect_version_id, the version the content read returned; a save built on an older version is refused with409 conflict,reason: stale_contentand the current version, and nothing is written. Without the field a save behaves as before (2026-09-29).GET /v1/recordings/{recording_id}answers a deleted recording410 gone(reason: deleted), a recording whose access ended under the workspace’s recording security410 gone(reason: access_ended), and a caller outside a members-only workspace403 forbidden(reason: members_only), signing nothing; every link it issues is recorded in the workspace’s audit log. The error vocabulary gainsgone(410), which an App-road refusal forwarded through the API used to report asinternal_error(2026-09-29, Call recordings, Errors and limits).GET /v1/agents/{id}returns the agent’s ownvoice(id,name) andlanguage(code,label); both were alwaysnull. One go-live check in itsreadinessblock is now namedartifact:phone-build, as the App’s Deploy tab names it (2026-09-29, Agents).GET /v1/calls/{call_id}andGET /v1/sessions/{call_id}(and the compatibility call-log reads) return the customer view of a call: each model leg is named by its Sempera model id, and the account id, credential source and upstream model the record held are no longer returned (2026-09-29).- A model leg that Sempera’s own cloud model pool served, which has no published Sempera model id, is named
sempera-cloud-modelon the hostcloudin those call and session reads and the compatibility call-log reads. They used to return the engine’s own name, its model and its host (2026-09-29). - The
resultofPATCHandDELETE /v1/agents/{id}/draftnames a cloud model pool engine in itsruntimeblock assempera-cloud-model, and lists every such engine as one option that cannot be chosen for a single agent; a draft that setsruntime_llm_backendtosempera-cloud-modelis refused with a sentence saying Sempera chooses the cloud model for the whole organization. Engines on Sempera’s own servers keep their names (2026-09-29). GET /v1/live-calls/streamnames each call’s language model inengines.llmby its Sempera model id, ornull(2026-09-29, Live calls and live transcripts).- A speech recognition or voice engine that a cloud provider hosts for Sempera is named
sempera-cloud-speech-recognitionorsempera-cloud-voiceinGET /v1/sessions/{call_id},GET /v1/calls/{call_id}and the compatibility call-log reads, and inengines.sttandengines.tts.provideronGET /v1/live-calls/stream. They used to return the engine’s own name and model. Engines on Sempera’s own servers keep their names (2026-09-29, Live calls and live transcripts). - The
resultofPATCHandDELETE /v1/agents/{id}/draftreads such an engine the same way in its hearing and speaking legs: the running engine (effective), its model and the speaking pace’sprovideruse the neutral name, and each such option’sreason_detailis the same sentence as itsreason. An option’svalueand the draft’s own values are unchanged, so a draft still names the engine by the value it read (2026-09-29). - Two refusal sentences name no engine a cloud provider hosts: a session, or a publish, that picks one where it is not cleared for real calls is refused with “The cloud voice engine …” or “The cloud speech recognition engine …” (the
409ofPOST /v1/sessionsstill carries the engine’s id inengine), and a recognizer that cannot choose the language per call is refused with “Auto-detect is not available with the speech recognition you chose.” (2026-09-29). - Why a call’s voice handed over to its backup voice is one fixed sentence in
GET /v1/sessions/{call_id}andGET /v1/calls/{call_id}: each handover’sreasoninruntime_actual.tts.fallbacksand insibling.tts_fallbacksreads “The voice engine stopped answering, so the backup voice took over.” It used to be the error’s own text, which could name the address the engine was reached on; the engines and voices of the handover are named as before (2026-09-29). - The draft’s summary, which previews each changed value now (
before) and in the draft (after), names a voice or speech recognition engine a cloud provider hosts assempera-cloud-voiceorsempera-cloud-speech-recognition, and a cloud model pool engine assempera-cloud-model, with their models, in theresultofPATCHandDELETE /v1/agents/{id}/draft, thesummaryofGET /v1/agents/{id}/draftand thedraftofGET /v1/agents/{id}. It used to preview the engine’s own name and model. The draft’s ownfieldsare unchanged, and engines on Sempera’s own servers keep their names (2026-09-29). POST /v1/sessionsnames the model of such an engine invoice.modelandspeech_recognition.modelassempera-cloud-voiceorsempera-cloud-speech-recognition. It used to return the engine’s own model.voice.providerandspeech_recognition.providerare still the engine ids the session was asked for (2026-09-29, Sessions and calls).GET /v1/personasandGET /v1/personas/{persona_id}report the agent’s own language, time zone and voice incall_config, its role asdescription, and real dates: when its first and newest versions were built, and the number and build time of the version it runs now, eachnullwhen the agent has none.orchestration_promptandpromoted_atarenull. They used to be fixed values (en,America/Toronto, an empty voice and description, version1and one placeholder date) whatever the agent was configured with (2026-09-29, Personas).GET /v1/recordings/{recording_id}looks the recording up before it signs a link: an id that does not exist and another workspace’s recording both answer404 not_found, and neither is signed. It used to sign a link for any id it was sent. A link a signed-in member asks for now plays for that member; it was bound to a form of their identity the audio path never matched, so it played for nobody. A link asked for with an API key still plays for nobody (2026-09-29, Call recordings).- A trigger’s webhook action survives a restart of the service between its attempts: the run keeps what its next attempt needs, and the next attempt is sent once the service is back, with the same event id and body, to the trigger’s endpoint while it is still enabled, three attempts in all. An attempt a restart cut off counts, marked as not known to have arrived. Such a run used to stay running until it showed as stalled and the owner sent it again (2026-09-29, Webhooks).
call.completedis also sent for an interpreted call (Live translation), once per call, withprovidersemperaandrecording_storedfalse, and the workspace’s Call completed triggers fire for it withdirectioninterpreted;GET /v1/sessions/{call_id}reads it. A session whose interpreter never started sends nothing. Interpreted calls used to send nocall.completedand fire no trigger (2026-09-29, Webhooks).call.completedis also sent for a phone call Sempera’s own call service answered, withprovidersempera, once per call whichever road sees it first, and never for a test session;GET /v1/calls/{call_id}reads such a call by the id the event carries. It used to be sent only for calls the carrier reported (2026-09-29, Webhooks).- A workspace’s Call completed triggers (
/v1/automation) also fire for a phone call Sempera’s own call service answered, once per call under the same claim ascall.completed, so a call both roads see fires them once; tests never fire them. They used to fire only for calls the carrier reported (2026-09-29, Webhooks). - An inbound text on an agent whose texting is off (Send text messages in its phone setup, which starts off) is stored and shown on its thread and not answered; the thread records why. It used to be answered on any bound number (2026-09-29, Text messages).
- With texting off in an agent’s phone setup (Send text messages, which starts off), the in-call text tool is refused and sends nothing, and the Send SMS voicemail action sends no text and records why. Both used to send whatever the setting said (2026-09-29, Text messages).
- Why an agent did not answer a text is a fixed sentence for the kind of failure (texting off, the model unavailable, the text not sent, and so on), on the thread, in the conversation drawer and on Needs attention. It used to include the engine’s or the carrier’s own error text, which could name the service it called (2026-09-29, Text messages).
agent.publishedcarries the sameagent_versionobject (id,object,agent_id,version_number,created_at,spec_hash,compiled_artifact_id) whichever road published. A publish from the App used to send the version ledger’s own row, with different key names and internal snapshot hashes (Webhooks).- A webhook endpoint whose stored signing secret cannot be read is never sent an unsigned request: the attempt fails in the delivery log with a sentence saying so. One such endpoint also no longer stops the workspace’s other endpoints from hearing the same event.
- Every webhook delivery goes over one guarded road (2026-09-28): the host is resolved and the connection pinned to the address that was checked, an address inside a private network is refused, and a redirect is a failed attempt. A saved endpoint URL with a private address literal, a user name or password, or a bad port is refused with
validation_failed. - A trigger’s webhook action names one of the workspace’s registered webhook endpoints and is signed with that endpoint’s own secret; a new
toolaction calls one of the workspace’s API request tools.POST /v1/automation/executions/{id}/retrysends a failed or stalled webhook or tool run again once, with the same event id (2026-09-28). - A call’s
call.completedandcall.outcomesdeliveries lose the words read from a call once its transcript is deleted: the stored bodies are emptied, and a later retry of acall.outcomesdelivery sends the emptied event (2026-09-26). - A version is named the same way everywhere (2026-09-28): the production pin on
POST /v1/callsresolves to the version the agent’s current build resolved to, not simply the newest ledger row, and a voice or phone call’s record names the version it started on, not the one current when it ended. - Editing a tool or a connection checks its name only inside its own workspace. Another workspace’s tool of the same name no longer refuses the edit or reveals that the name exists elsewhere (2026-09-28).
- Evaluation criteria are read and scored from the caller’s own workspace only (2026-09-27).
openapi.jsonnames no provider: an operation id that carried a platform name is nowlistCitationSchemasListPath, and a compatibility path whose name carries that platform’s name is still served but no longer listed.openapi.jsondeclares thebranch_idpath parameter (a UUID) on the four/v2/agents/{uuid}/branches/{branch_id}routes (rebase preview, rebase, merge and delete), which listed only the agent (2026-09-29). The routes themselves did not change.
Added
- In the App, a text conversation shows every message on its thread in order, who sent each and when, and a texter left without an answer is an urgent item on Needs attention that counts toward the attention alert (2026-09-29, Text messages).
- Outbound call limits (2026-09-28): an owner can cap the calls a workspace places per UTC hour and per UTC day.
POST /v1/callsover a cap answers429 rate_limitedwithretry_after_s, and503 service_unavailablewhen a cap cannot be read; nobody is called (Errors and limits, Owner features in the App). - A real phone call answered by Sempera’s own call service over the carrier’s audio stream (2026-09-27): two provider-ingress routes carry it, public only on a deployment that turned live call audio on (Webhooks).
- Owner features in the App, described in Owner features in the App: filtered call-data exports built in the background and downloaded by owners; outbound batches with a start time, CSV recipients and a background scheduler; per-tool analytics over 7, 30 or 90 days; an Import from cURL panel on the tool form; writing, testing, publishing and pinning code snippets from the Tools page.
- Text messages (2026-09-28): the agent’s published version answers inbound texts on a thread it holds, in place of a fixed reply (Text messages).
- Developer guide pages for snippets, triage, dispositions, alerts, widgets, text messages and branches, verified against the route table, and
app-features.md. The guide saidPOST /v1/callswas not offered and answered501; it records a one-call outbound batch and answers201, and Sessions and calls now says so.webhooks.mdis rewritten as the stable outbound contract: the events, each payload, signing with a test vector, retries, idempotency, ordering and the delivery log. - Developer guide pages for automations, scenarios, secrets and request signing, live calls and live transcripts, recordings, personas, citation schemas and the caller memory schema (2026-09-29), each checked against the route table and its handlers. They record behaviour as it is, including that the live streams answer one snapshot frame and close, that
GET /v1/recordings/{recording_id}signs a link an API key cannot fetch, and which persona fields are fixed values.
changes since the cutover
Recorded from the route table and its history between 2026-09-15 and 2026-09-27.
openapi.json is the full contract.Changed
GET /v1/tools,GET /v1/integrationsandGET /v1/voices(2026-09-21) answer compatibility envelopes instead of the standard list envelope:{"status", "tools", "hasMore", "nextCursor", "data", "errors"},{"data": {"count", "integrations"}, "errors"}and{"voices": [...]}. Thekindsandengineskeys those lists carried are gone. This changed/v1without the notice the policy above promises; this entry is the record of it.session.completedis retired. It was accepted as a subscription but never sent; asking for it is nowvalidation_failed, and endpoints that stored it no longer list it (Webhooks).test.completedis now sent when a test run finishes or crashes (2026-09-26).
Added
- Agent branches under
/v2/agents/{agent_id}/branches(Agent branches). - Dispositions and their extractors under
/v2/agents/{agent_id}/dispositions; published dispositions run on completed phone calls and their results ride oncall.outcomesasdispositions(Dispositions). - Triage issues under
/v1/triage: issues, flagged calls, files, comments, activity and links between issues (Triage issues). - Metric alert rules under
/v1/alerts, and themetric_alert.triggeredevent, delivered only to the endpoints a rule names (Metric alerts). - Code snippets under
/v1/snippets, and thecodetool kind that pins a published snippet version (Code snippets). - Website widgets:
GET /v1/widgets,GET /v1/widget/threadsand/v1/widget/custom-components(Website widgets). - Text messages:
GET /v1/sms/numbersandGET /v1/sms/conversations(Text messages). - Automation triggers and their executions under
/v1/automation. - Evals:
GET /v1/evals/agents,/v1/evals/agent-templates,/v1/evals/test-cases,/v1/evals/user-templates(andPOST), andPOST /v1/evals/quick-run(Tests and evals). - Speech models and saved prompts:
GET /v1/models,GET /v2/tts/models, the credential-freeGET /v2/models, and/v1/prompts(Sessions and calls). - Workspace secrets (
/v1/secrets), JWT request signing (/v1/signing,/v1/signing/keysandGET /v1/workspaces/{workspace_id}/.well-known/jwks.json) andGET /v1/release-deployments. - Live calls and recordings:
GET /v1/live-calls/stream,GET /v1/live-calls/transcript/streamandGET /v1/recordings/{recording_id}. GET /v1/personas,/v1/citation_schemas,GET /v1/agents/{agent_id}/memory/schema, scenarios under/v2/agents/{agent_id}/scenarios, and environment variables under/v2/agents/{agent_id}/environments/{env}/variables.POST /v1/mcpandGET /v1/mcp/clients/{client}for coding agents (Getting started).- Further compatibility facades, tagged
Compatibilityinopenapi.json.
the one-API cutover
- Compatibility
/v2routes for the console agent, disposition, deployment, batch and analytics reads were added behind the same credential and workspace boundary as/v1. They are facades over Sempera-owned stores, not a separate database. api.sempera.orgbecame the single canonical Sempera API, versioned under/v1, withopenapi.json,llms.txtand these guide pages served from it.- The private server hostname (the retired legacy API hostname, retired 2026-09-14) was migrated behind the canonical API. No customer or developer integration ever targeted it.
- The existing operational routes that the live salon agent’s tools call (
/v1/caller/*,/v1/catalog/*,/v1/center/*,/v1/booking/*,/v1/messages) remain in service unchanged as the runtime tool surface; they are internal to Sempera-built agents, are not part of the public contract, and will be folded into Tools when the salon agent is next re-certified. - Credential classes (developer, runtime, CI, service, admin keys; the browser session token) were introduced with explicit scopes.
- Sempera-signed outbound webhooks were introduced (
X-Sempera-Signature).