API changelog
Integration-relevant changes to the Vorbal API
Check this page for public API changes that can affect your integration and the actions you need to take.
How changes are published
- Added identifies a new, backward-compatible capability.
- Changed identifies a contract or behavior change and includes migration guidance when needed.
- Fixed identifies an implementation or documentation correction.
- Deprecated identifies a supported capability that you should no longer use and includes its replacement and planned removal timeline.
Use the endpoint reference for the current contract. Use this changelog to understand how that contract differs from an earlier integration.
August 31, 2026
Added
-
Secure card payments. Use
POST /v1/sessions/{uuid}/payment/card/collectandPOST /v1/sessions/{uuid}/payment/cvv/collectto receive call-scoped opaque references without receiving card or CVV digits. A successfulpayment.card.collectedwebhook includes acard_uuidreference in thecard_<lowercase-uuid-v4>format and safe display values underpayment_method_details.card.last4and.brand. A successfulpayment.cvv.collectedwebhook includes acvv_uuidreference in thecvv_<lowercase-uuid-v4>format. Invalid input produces a sanitized outcome, such asreason: "luhn_check_failed", without disclosing the rejected digits.Secure collection and Room membership are mutually exclusive. Starting collection for a Session that is a Room member, or adding a Session to a Room while collection is active, returns
409 Conflict. While collection is active, the platform also rejects new Recordings andunmaskrequests. After the terminal collection webhook arrives, each Recording that the platform masked or paused remains in that state; call the Recording’sunmaskendpoint when the application is ready to resume writing sensitive-free audio.Submit the opaque references to
POST /v1/sessions/{uuid}/payment/chargeswhile the Session is connected. Include a requiredIdempotency-Key,exp_month,exp_year, a positiveamountin the currency’s smallest unit, lowercasecurrency: "ils",provider: "nedarim", and the customer’s seven-digitterminal_number. Nedarim accepts only whole-ILS amounts, so an ILS amount in agorot must be divisible by 100.A charge’s first provider attempt atomically consumes its
card_uuidandcvv_uuid. An exact replay with the sameIdempotency-Keyreturns the original sanitized HTTP result and terminal outcome. A new operation cannot reuse either reference, so collect new card and CVV references before another charge attempt. An unused reference expires no later than one hour after collection, and Session cleanup normally removes it sooner.The terminal
payment.charge.completedwebhook reports the trusted amount, currency, provider, terminal number, status, and retryability. A successful result can also include the transaction creation time,receipt_created, validated card details, installment count, and normalized transaction type, card classification, issuer, and acquirer. Usestatusand the originatingoperation_uuidto correlate the outcome. The webhook excludes provider transaction identifiers, authorization codes, voucher numbers, UIDs, raw responses, provider-authored prose, PAN, CVV, expiry, identity numbers, customer PII, receipt URLs, and bearer capabilities.The platform durably stages each accepted collection result. A service interruption or Session end completes the staged result, or emits a terminal failed result if collection cannot be recovered. A charge timeout is reported as
indeterminatewithretryable: falseand is never automatically retried.
Changed
-
Nedarim terminals must be assigned to the calling application. Continue sending the customer’s seven-digit
terminal_numberwith every charge; the value is not a secret and is not replaced by a server-side default. The platform now verifies that the terminal is assigned to the authenticated application before accepting the charge. An unassigned terminal returns403 Forbiddenwithout creating a charge operation. Ask the platform operator to assign each customer’s terminal before sending its first charge. -
Every accepted charge retains a terminal outcome across Session end. If the Session hangs up or a service restarts before provider processing begins, the platform emits a terminal failed outcome. If provider submission already began and the result cannot be proven, it emits
indeterminatewithretryable: false; a stored sanitized completion remains replayable. Do not create a replacement charge merely because the Session ended. Wait for and deduplicatepayment.charge.completedbyoperation_uuid.
Fixed
- Standalone WebSocket lifecycle webhook delivery. The platform again
delivers
websocket.connected,websocket.disconnected,websocket.failed, andwebsocket.audio_playback_completedfor standalone WebSocket Sessions. The last confirmed working configuration was August 18, 2026 at 01:31 Israel time; the first affected configuration was August 21, 2026 at 04:30 Israel time. During that interval, these events could be omitted before webhook delivery, so there was no webhook HTTP response or stable error code. For example, an expected redacted event such as{"event":"websocket.disconnected","session_uuid":"sess_..."}could be absent. Missed historical events are not replayed. No request or webhook schema changed, and no migration is required; continue normal webhook handling and at-least-once deduplication.
August 30, 2026
Added
-
Optional inclusion of recording gaps.
POST /v1/sessions/{uuid}/recordingsnow accepts the optional booleaninclude_gaps, which defaults tofalse. Omit it or set it tofalseto remove redacted periods and periods without recordable audio from the artifact. Set"include_gaps": trueto include those periods as silence. The setting applies only to that Recording and is returned by the Recording list and inspection endpoints. Existing requests do not need to change. -
Session hold-state webhooks. Applications now receive
session.heldwhen a Session enters hold andsession.resumedwhen it returns from hold. Both events carrysession_uuidin addition to the standard webhook fields. Existing integrations do not need to change. Add either event to your webhook handler when your application needs to track whether a Session is on hold. -
MP3 file playback. Session Playback, Room Playback, and optional DTMF collection prompts now accept finite HTTP or HTTPS MP3 URLs in addition to WAV URLs. Submit the original
.mp3URL inurlsorprompt_files; no client-side conversion or alternate filename is required. Existing WAV integrations are unchanged. -
Opt-in Room voice-activity events.
POST /api/v1/roomsnow accepts the optional booleanvoice_activity_events. Set it totruewhen the Room must emitroom.member.voice_activity_changedas members start and stop talking. It defaults tofalse, so existing Room creation requests continue to work without receiving these high-frequency events. The Room resource reports the selected value. The event was previously documented without an activation control, although Rooms did not emit it. If your integration was built from that earlier documentation and expects voice-activity events, add"voice_activity_events": truewhen creating each Room; existing Rooms must be replaced with Rooms created with the option enabled. -
Room-member volume results.
POST /v1/sessions/{uuid}/room/volumenow reports its asynchronous result through exactly one terminal webhook.room.member.volume_changedconfirms that every requested direction was applied.room.member.volume_change_failedreports an execution failure and includesapplied_directionsandfailed_directions, so adirection: bothrequest can report a partial result without implying an atomic two-direction change. Both events include the submittedvolumeanddirection, plus thesession_uuid,room_id, and originatingoperation_uuid. -
Reject inbound calls or cancel outbound dialing before answer. Use
POST /v1/sessions/{uuid}/rejectwith a requiredreasonofnumber_not_found,invalid_number,timeout,busy, orrejectedfor an unanswered inbound phone call. UsePOST /v1/sessions/{uuid}/cancelto stop an outbound phone call before the callee answers. Both operations return202 Accepted, followed by the existingsession.endedwebhook. For a rejection, that webhook reports the selectedreasonunchanged ashangup_cause. Both operations return409 Conflictfor an incompatible Session or lifecycle state. UseDELETE /v1/sessions/{uuid}when you want to disconnect regardless of answer state.
Changed
-
Session Playback request variants are now named separately in OpenAPI. The
POST /v1/sessions/{uuid}/playback/playrequest remains oneoneOfcontract, with named schemas for file playback, pushed audio, and silence. Generated-audio uploads useSessionPlaybackPushRequest. This schema-organization change does not alter endpoint URLs or wire payloads beyond the changes documented separately below, and requires no additional migration. -
Breaking: generated-audio Session Playback uses
type: pushand an HTTPS streaming upload.POST /v1/sessions/{uuid}/playback/playnow distinguishes audio pushed by the application from live HTTP/HTTPS URL sources. Sendtype: pushwith the raw audio format. The202 Acceptedresponse includes aningestobject with the short-lived URL, required HTTP method and headers, and expiry. Follow that object to stream raw mono audio in one request body. Authenticate the ingest request with the same application Bearer credentials that you use for other Voice API requests. The response does not repeat those credentials iningest.headers. There is no compatibility alias: a Session request that usestype: streamwithmedia_formatnow returns400 Bad Request.Replace flat upload metadata with the nested ingest contract:
The initial documentation described the upload as a WebSocket and later as a streaming
PUT. Replace either transport with the returned ingest contract:Supported formats are little-endian PCM16 at 8, 16, 24, or 48 kHz and G.711 mu-law at 8 kHz. The body is unframed raw audio. Close it after the final audio bytes. A 2xx ingest response confirms that the platform accepted the complete request body; it does not confirm that the caller heard all audio. The Voice API verifies that the authenticated application owns the Playback operation before it accepts the upload. Do not send Voice API credentials to any URL other than the Voice API origin that your integration is configured to use.
type: streamremains the discriminator for live HTTP/HTTPS URL playback, such as radio. Room Playback keeps its{"type":"stream","url":"https://..."}request shape; live Room streams remain unsupported and return502. This entry supersedes the earlier August 22 statement that live streaming upload was not part of the contract. -
Breaking: Session recordings are addressable resources. A Session can now own multiple concurrent recordings, including recordings with identical settings. Each accepted create request returns a new
recording_uuid, and subsequent inspection and control requests address that exact Recording. There is no singleton Recording, implicit “current recording,” or stop-all operation. Update endpoint usage as follows:The old endpoints have been removed without compatibility aliases. Persist the
recording_uuidreturned by the create request and use it for later commands. UseGET /v1/sessions/{uuid}/recordingsto reconcile active or stopping recordings, orGET /v1/sessions/{uuid}/recordings/{recording_uuid}to inspect one live Recording. These endpoints do not provide recording history.Create requests now require
direction(both,session_in, orsession_out) andchannels(monoorstereo). The initial release accepts only{"direction":"both","channels":"mono"}; other valid combinations return HTTP400as unsupported.Voice-activity events remain optional on each create request and default to disabled. Each Recording configures voice-activity detection independently, so multiple concurrent Recordings may enable events with different
voice_activity_configvalues. Each Recording receives its own events, correlated byrecording_uuidand the originatingoperation_uuid. Stopping one Recording stops only its events and does not affect the other active Recordings. -
Breaking: WebSocket failure reason renamed. The
websocket.failedwebhook now reportsreason: connection_precondition_failedinstead ofrelay_precondition_failedwhen the platform cannot satisfy a prerequisite for opening the WebSocket Session connection. Update enum validation and failure handling to use the new value; there is no compatibility alias. The event name,errorbucket, HTTP status semantics, and other reason values are unchanged. -
Room-wide webhook ordering. The platform now delivers all
room.*events for one Room in production order. Failures block only that Room. Payloads are unchanged, and no migration is required. -
Breaking: Room member volume directions. The
directionfield inPOST /v1/sessions/{uuid}/room/volumerequests and the correspondingdirection,applied_directions, andfailed_directionswebhook fields now use names relative to the Room member. Renameintofrom_memberandouttoto_member;bothis unchanged.from_memberis the member’s audio sent into the Room, whileto_memberis the Room mix sent to the member. WebSocket media-format fields remainfrom_sessionandto_sessionbecause those directions are relative to the Session. -
Silence playback admission is fully asynchronous.
POST /v1/sessions/{uuid}/playback/playwithtype: silencenow returns202 Acceptedafter durable workflow admission, without waiting for the earlier playback stop to complete. The platform still stops existing playback before starting indefinite silence, but performs that sequence in the background and reports progress or failure through the existing playback webhooks. Treat202as admission only. A silence request no longer returns503solely because confirmation of the preceding playback stop was not received during the HTTP request. -
Playback start webhook timing. For file playback, the platform now publishes
playback.startedat a later point in startup, using a best-effort estimate that playback of the first source has begun. Previously, the event followed an earlier source-preparation signal.
Fixed
-
One logical terminal Session event per call. The platform now creates one
session.endedevent when multiple termination signals or recovery checks observe the same call ending. The event uses the publicsession_uuid; an internal call identifier is no longer emitted as a separate Session event. The event schema is unchanged, and no migration is required. Webhook delivery remains at least once, so continue to deduplicate deliveries by(aud, Webhook-Id). -
Terminal event after unrecovered playback media failure. If playback media fails after a request is accepted, the platform first attempts to recover it. A successful recovery remains transparent. If recovery is exhausted, the platform now emits the operation’s existing terminal event:
playback.failedwithreason: media_unavailablefor Session playback,room.playback.failedfor Room playback, ordigits.collectedwithstatus: failurefor prompt-and-collect. Failures before acceptance remain synchronous HTTP errors and do not emit these webhooks. Treat webhook delivery as at least once and deduplicate by(aud, Webhook-Id).
August 29, 2026
Changed
- Breaking: recording media URL renamed. Recording API responses and
webhook payloads now expose
media_urlinstead ofpull_url. Update your response and webhook models to readmedia_url; there is no compatibility alias. The URL behavior and expiry are unchanged: a GET starts at byte zero and follows the growing recording until EOF. After finalization, the same URL serves a finite, range-capable WAV.live_urlis unchanged and continues to join near the current position.
August 28, 2026
Added
- SMS Messages API. Send outbound SMS with
POST /v1/messages, retrieve a message’s latest status withGET /v1/messages/{message_uuid}, and list messages withGET /v1/messages. Use Message webhooks for status updates.
Changed
-
Breaking: retry timing moved to the standard response header. Public HTTP error response bodies no longer include the top-level
retry_afterfield. Retry timing remains available in the standardRetry-Afterresponse header for429rate-limit and529overload responses. Those JSON bodies now keep the uniformcodeandmessageshape, and their human-readable messages end withSee Retry-After header.Update your response model and retry handling as follows:
Continue to branch on the stable
code, not the human-readablemessage. This change does not remove unrelated error fields: playback source failures retaindetail, and unexpected500responses retainsupport_id.
Fixed
- Session command bodies with idempotency keys. Session commands that send
a nonempty JSON request body with
Idempotency-Keyno longer reject the valid body as invalid JSON.
August 27, 2026
Added
- Read application configuration. Authenticated applications can use
GET /api/v1/appto read their assigned inbound phone numbers, permitted outbound caller IDs, webhook URL, blocked webhook events, and effective Room limits. Useinbound_phone_numbersandoutbound_caller_idsto populate your own number-assignment interface. TheGEToperation is read-only: it does not assign numbers or expose unassigned numbers or another application’s configuration. Existing integrations do not need to change.
Changed
-
Breaking: application configuration replaces webhook-specific routes. Replace
GET /api/v1/app/webhookwithGET /api/v1/app, and replacePUT /api/v1/app/webhookwithPATCH /api/v1/app. Use the PATCH request to setwebhook_url. A successful update returns204without a response body. Use the generic GET operation to verify the saved setting. The removed routes have no compatibility aliases. -
Breaking: HTTP response fields standardized. All Voice Platform HTTP API responses, including successful and error responses, no longer include the transport-level
successfield. Use the HTTP status code to determine whether a request succeeded or failed, and removesuccessfrom your response models and checks.Error responses now use top-level
codeandmessagefields. Rename the previouserrorstring tomessage; for example, change{"success":false,"error":"...","code":"invalid_request"}to{"code":"invalid_request","message":"..."}. Unexpected-error fields are also top-level, so change{"error":{"code":"internal_error","message":"...","support_id":"..."}}to{"code":"internal_error","message":"...","support_id":"..."}. At the time of this change,detailandretry_afterremained available when applicable. Genuine domain lifecyclestatusfields are unchanged. -
Breaking: specific Authorization header error code. A missing or malformed application
Authorizationheader now returns HTTP400with codeinvalid_authorization_headerinstead ofinvalid_request. Update any error handling that matches these header failures by code. Themessageidentifies whether the header is missing or invalid and shows the expected format:Bearer <app_uuid>:<api_key>. Use the app UUID and API key issued to your application. A credential with that structure but a rejected API key remains HTTP401 unauthorized; other invalid requests remaininvalid_request. -
Breaking: one request-validation error per response. Request-schema validation failures now return exactly one issue with top-level
codeandmessagefields. The message identifies the relevant request path and explains how to correct it. For example:{"code":"invalid_request","message":"The \"to\" phone number must match E.164 format"}.Validation responses no longer include
success,error, bodystatus,detail,errors,param,location, orvalue. Readcodeandmessageand stop parsingdetail.errors. When a request has multiple issues, fix the returned issue and retry to reveal a later issue. The platform selects the returned issue deterministically according to the product contract, not the validator’s raw finding order. -
JSON request header compatibility. Continue to send
Content-Type: application/jsonwith JSON request bodies. For compatibility, the platform now tolerates a missing or blankContent-Typeheader and attempts JSON parsing. This does not add another supported request media type. An explicitly supplied unsupported or malformed media type is still rejected with HTTP415. -
Clearer Dial validation errors. Invalid request bodies now return
422 application/jsonwith one readablemessage. Runtime phone-policy errors remain400 invalid_request.
August 26, 2026
Changed
- Choose when recording starts.
POST /v1/sessions/{uuid}/recording/startnow accepts the optional booleanstart_only_after_answer. Omit it or set it tofalseto start as early as media is available; the recording may include ringing or early media. Set it totrueto wait for canonical answer.
August 24, 2026
Changed
- Breaking: standalone WebSocket event names. Standalone WebSocket Session
lifecycle events now use the public
websocket.*namespace instead of the removed sidecar relay’s internal name. Update webhook filters as follows:websocket_relay.connected→websocket.connectedwebsocket_relay.disconnected→websocket.disconnectedwebsocket_relay.failed→websocket.failedwebsocket_relay.audio_playback_completed→websocket.audio_playback_completedThis pre-launch change has no compatibility aliases. Payload fields and delivery behavior are unchanged.
August 23, 2026
Added
- G.711 mu-law relay audio. WebSocket relay
media_format.from_sessionandmedia_format.to_sessionnow independently acceptencoding: "mulaw"with the requiredsample_rate: 8000. Binary messages contain raw mono G.711 mu-law bytes without a WAV header, JSON, or base64 wrapping. Existingpcm_s16leintegrations do not need to change. This adds the audio encoding only; it does not implement the Twilio Media Streams message protocol. - Custom WebSocket ports. External WebSocket URLs now accept any TCP port
from
1through65535for bothws://andwss://. Ports other than the scheme defaults must be explicit. You can omit the port:ws://defaults to80, andwss://defaults to443. This applies to thewebsocketobject accepted byPOST /v1/sessions:connectWebsocket. You can connect a standalone WebSocket Session directly to a public server on a custom port, such aswss://media.example.com:8443/audio. This change supersedes the August 21 requirement to include an explicit default port.
Changed
-
Breaking: sidecar WebSocket relay removed. The API no longer registers
POST /v1/sessions/{uuid}/websocket-relay,DELETE /v1/sessions/{uuid}/websocket-relay, or thePOSTroutes ending in/pause,/resume,/mute,/unmute, and/flush. Requests to those paths return404, and the operations are absent from the OpenAPI endpoint reference. This pre-launch removal has no compatibility alias or deprecation period. For passive call audio, use the Recording API and its signedpull_urlorlive_url. For a bidirectional agent, create a standalone WebSocket Session and add it with the phone Sessions to a Room. For one-way audio, use Session or Room Playback. Live streaming upload is not part of the current contract. -
Breaking: Finite-file requests to
POST /api/v1/rooms/{room_id}/playback/playnow requiretype: "files". The endpoint no longer treats an omittedtypeasfiles; requests withouttypereturn HTTP400with codeinvalid_request. Change{"urls":[...]}to{"type":"files","urls":[...]}. Requests for a live stream continue to requiretype: "stream"andurl. -
Breaking: explicit request choices. The API no longer supplies default values for the non-boolean request fields below. Requests that omit a required field return HTTP
400with codeinvalid_request. To preserve the previous behavior, send the former value explicitly:For DTMF collection, an explicit
"terminators":[]now disables terminators. Send"terminators":["#"]to preserve the previous behavior. Boolean fields whose omitted behavior isfalseremain optional. This includesenable_voice_activity_events,muted,loop, andstart_muted. Sendtrueonly when you want to enable the corresponding behavior.
August 21, 2026
Added
- HTTP/2 support. The public Voice API edge now negotiates HTTP/2 over TLS with ALPN and keeps HTTP/1.1 connections reusable. Existing HTTP/1.1 integrations do not need to change requests or authentication. Clients can use HTTP/2 multiplexing or reuse one HTTP/1.1 connection instead of opening a connection per request. HTTP/3 is not supported by this edge.
Changed
- Breaking: playback source errors. Synchronous
502failures while admitting a playback or DTMF prompt source now returncode: playback_source_errorinstead of the genericcode: unavailable. The response includes bounded diagnosticdetail:reasonis one ofunsupported,not_found,access_denied,timeout,unreachable,rejected,invalid_response, orpreparation_failed.source_indexidentifies the failing zero-based item when the request contains multiple URLs.retryablesays whether the same source may succeed if retried later.upstream_statuscontains the source origin’s HTTP status when one was received. Update integrations that branch oncode: unavailablefor these502responses to branch onplayback_source_errorand usedetail.reasonfor handling. The API does not return source URLs, credentials, raw upstream response bodies, filesystem paths, or media-engine details.
Fixed
- Plaintext webhook and WebSocket URLs. Webhook delivery URLs accept both
http://andhttps://, and WebSocket relay URLs accept bothws://andwss://. Plaintext schemes do not require an operator opt-in flag. Relay URLs must include a nonempty path. At the time of this change, relay URLs also required an explicit default port. The August 23 change supersedes that port requirement. Existinghttps://webhook andwss://relay configurations do not need to change. Usehttps://andwss://unless you explicitly accept exposing webhook payloads and signature headers, or call audio and relay connection headers, without transport encryption. Both URL types remain subject to address-safety checks; the host must resolve to a permitted public address.
August 20, 2026
Added
- WebSocket relay failure reasons. The
websocket_relay.failedwebhook event now carries areasonfield: a closed, stable enum that classifies why the relay could not be established. It is delivered alongside the existingerrorandhttp_statusfields, which are unchanged. Existing consumers that only readerrorandhttp_statusdo not need to change. Thereasonvalues are:session_codec_unsupported— the active call is not carried in an audio codec the relay supports. This is a platform-side precondition, not a problem reaching your WebSocket server.destination_unreachable— the target WebSocket server could not be reached (DNS resolution, TCP connection, or routing failed).tls_failed— the TLS handshake to the target WebSocket server failed.handshake_rejected— the target returned a non-success HTTP status to the WebSocket upgrade request.relay_precondition_failed— a precondition for starting the relay was not met. Switch onreasonfor actionable handling rather than parsingerror. Treatsession_codec_unsupportedas a platform-side precondition failure, not a network problem on your side.
Fixed
- Faster finite WAV playback start. Session Playback for finite PCM WAV
files now starts without first reading the complete file. Existing playback
requests do not need to change;
playback.startedmay arrive sooner, especially for larger files or files hosted on slower origins.
August 19, 2026
Changed
- Breaking: caller-supplied operation IDs. For applicable asynchronous
commands, move caller-supplied correlation from body or query
operation_uuidto the optionalOperation-Idheader. Responses and webhooks continue to useoperation_uuid;Idempotency-Keyremains separate for retry deduplication.
Fixed
- Sidecar WebSocket playback limitation. The API reference now documents a playback limitation: you cannot play a file while a sidecar WebSocket is connected to the same Session. We are working to remove this limitation. Disconnect the sidecar WebSocket before starting Session Playback. To play both sources at once, add a standalone WebSocket Session and the phone Session to the same Room, then use Room Playback.
- JSON route errors. Unknown public paths and unsupported HTTP methods on
known public paths now return the standard JSON error envelope with
Content-Type: application/jsoninstead of plain text. A route-level404usescode: not_found; a405usescode: invalid_request. Integrations that special-cased these plain-text responses can now parse them like other API errors and branch oncode.
August 15, 2026
Changed
- Breaking: Standard Webhooks signing. Webhook delivery now follows
Standard Webhooks with Ed25519.
Update delivery headers and verification as follows; the former headers and
signing input are no longer sent or accepted:
Idempotency-Key(delivery) →Webhook-Id- signature timestamp field
t=→Webhook-Timestamp X-Webhook-Signature: t=...,akid=...,v1a=...→Webhook-Signature: v1a,...X-Webhook-App-UUID→ required signed body claimaudv1a\n<t>\n<app_uuid>\n<idempotency_key>\n<raw_body>→<webhook-id>.<webhook-timestamp>.<raw-body>Requireaudto equal the configured application UUID after signature verification and before side effects. During key rotation,Webhook-Signaturecan contain two space-delimitedv1asignatures; accept when at least one verifies against the active public-key set. The incoming API request headerIdempotency-Keyis unchanged.
- Breaking: WebSocket relay resource. The per-session WebSocket relay is
now a singleton resource.
Update request URLs and methods as follows; the former command routes are no
longer accepted:
POST /v1/sessions/{uuid}/websocket/start→POST /v1/sessions/{uuid}/websocket-relayPOST /v1/sessions/{uuid}/websocket/stop→DELETE /v1/sessions/{uuid}/websocket-relayPOST /v1/sessions/{uuid}/websocket/{pause|resume|mute|unmute|flush}→POST /v1/sessions/{uuid}/websocket-relay/{pause|resume|mute|unmute|flush}
- Relay deletion requests. Send relay deletion without a request body. To
supply your own correlation
identifier, send the optional request header
Operation-Id: <lowercase-uuid-v4>; otherwise, the platform generates one. The202 Acceptedresponse returns the selected value in itsoperation_uuidfield.Operation-Idis the only request input that selects this correlation value; unrelated query parameters receive no special handling. Continue to useIdempotency-Keyseparately when retrying the same deletion. - Breaking: WebSocket text messages. Custom text messaging over the
WebSocket relay is no longer
supported in either direction. The removed
final_textdeletion property has no replacement, andwebsocket.message_receivedis removed without a replacement event. Exchange application media as binary audio frames only. A backend text frame that is not a platform-reserved relay control closes the WebSocket connection with code1003; reserved controls are internal and are not an application extension point. - WebSocket relay event names. WebSocket relay lifecycle events moved from
the generic
websocketnamespace to the relay resource namespace. Update webhook filters as follows:websocket.connected→websocket_relay.connectedwebsocket.disconnected→websocket_relay.disconnectedwebsocket.failed→websocket_relay.failedwebsocket.audio_playback_completed→websocket_relay.audio_playback_completed
- Superseded: webhook delivery IDs. This intermediate pre-launch contract
moved the platform-generated delivery key from
X-Idempotency-KeytoIdempotency-Key. Under that contract, the key value and Ed25519 signing preimage were unchanged, and the fourth preimage line remained the unmodified delivery-key value. The delivery header was distinct from the client-generatedIdempotency-Keyon supported API mutation requests. The Standard Webhooks change above superseded this contract; useWebhook-Idfor the final delivery header.
August 12, 2026
Added
- Room Playback correlation. Every Room Playback command now accepts an
optional client-generated
lowercase UUID v4 as
operation_uuid. When supplied, the API echoes that exact value in the command’s202 Acceptedresponse; when omitted, the platform generates one. Use it for correlation, not request deduplication. - Idempotent Room Playback pause and stop.
POST /api/v1/rooms/{room_id}/playback/pauseandPOST /api/v1/rooms/{room_id}/playback/stopnow supportIdempotency-Key, completing idempotent retry support across Room Playback commands. Keepoperation_uuidstable for correlation and reuseIdempotency-Keyseparately when retrying the same request.
Changed
- Breaking: WebSocket media format. WebSocket relay requests now require a
media_formatobject for each audio direction. This applies toPOST /v1/sessions/{uuid}/websocket/startand thewebsocketobject accepted byPOST /v1/sessions:connectWebsocket. Replace the removed fields as follows:from_session_audio_rate→media_format.from_session.sample_rateto_session_audio_rate→media_format.to_session.sample_rate
- Required PCM encoding. Add
encoding: "pcm_s16le"to both directions. Both directions, encodings, and sample rates are required, with no defaults. Sample rates must be multiples of8000from8000through192000. Old flat fields and other encodings are rejected; additional encodings are planned but not yet accepted.
Fixed
- Room Playback terminal correlation.
room.playback.endedandroom.playback.failednow always carry a non-empty, lowercase UUID v4operation_uuidfor the originating play request. The platform suppresses a malformed terminal event instead of delivering one whose correlation identifier is missing or invalid. Consumers may validate this required field as a UUID. - WebSocket audio wire format. Both directions use raw signed 16-bit little-endian mono PCM in binary frames. Send samples without a WAV header and decode received frames at the configured sample rate.
August 11, 2026
Added
- Promptless DTMF collection. DTMF collection can now run without prompt
playback. Omit
prompt_filesfromPOST /v1/sessions/{uuid}/dtmf/collectto collect input without playing audio first. Existing requests withprompt_filesstill play those prompts before collecting input.
Changed
-
WebSocket connection headers. The WebSocket relay request property
headersis nowconnection_headersforPOST /v1/sessions/{uuid}/websocket/startand thewebsocketobject accepted byPOST /v1/sessions:connectWebsocket. Update serialized request bodies;headersis no longer accepted. -
Reserved WebSocket headers.
connection_headersaccepts only customer-controlled HTTP headers. Requests are rejected when the map containsUser-Agent,Origin,Host,Connection,Upgrade,Sec-WebSocket-*,Content-Length,Transfer-Encoding,Proxy-*,Forwarded,X-Forwarded-*,X-Real-IP, orCF-Connecting-IP, case-insensitively. The platform sendsUser-Agent: Voice-Platform/1.0and derivesOriginfrom the target WebSocket URL. -
Operation resource removed. The API no longer models command acknowledgements as a separate
Operationresource.operation_uuidis unchanged in requests, responses, and webhooks; it remains an opaque token for correlating lifecycle events.
August 10, 2026
Added
- Send DTMF. Applications can send ordered DTMF keypad presses and timed
waits to the
remote party on an answered phone session with
POST /v1/sessions/{uuid}/dtmf/send. Only one sequence can run per session; ringing calls and standalone WebSocket sessions are unsupported.202 Acceptedis the sole admission acknowledgement and does not confirm remote handling. - Cancel DTMF. Applications can cancel the active outbound DTMF sequence
with
DELETE /v1/sessions/{uuid}/dtmf/send. A key already submitted for transmission may finish, but later keys and waits are discarded.202 Acceptedis the sole cancellation acknowledgement; no terminal webhook is emitted.
Changed
- Command admission webhooks.
command.*.acceptedadmission webhooks are no longer emitted. Treat202 Acceptedas the sole admission acknowledgement and use later domain lifecycle webhooks only for observed state changes or outcomes. Removecommand.*.acceptedfilters; correlate lifecycle events throughoperation_uuidwhen present. - DTMF collection routes. DTMF collection routes moved to the
Keypad API namespace. Update request
URLs as follows; the former routes are no longer accepted:
POST /v1/sessions/{uuid}/play_and_get_digits→POST /v1/sessions/{uuid}/dtmf/collectDELETE /v1/sessions/{uuid}/play_and_get_digits→DELETE /v1/sessions/{uuid}/dtmf/collect
- Recording silence duration. Session recording voice-activity detection
now accepts
silence_duration_msup to3,600,000milliseconds (one hour), increased from10,000. Existing values remain valid. Use the larger range to tolerate extended quiet periods beforerecording.speech.ended.
Fixed
- WebSocket relay restart. Starting a WebSocket relay after
websocket.disconnectedorwebsocket.failednow reliably creates a fresh relay instead of failing on a remnant of the previous connection. Applications can retryPOST /v1/sessions/{uuid}/websocket/startafter either event without first sending a stop command. - WebSocket URL terminology. WebSocket relay URLs now consistently document
ws://andwss://as accepted schemes and call the remote endpoint the WebSocket server. This is a documentation correction; existing relay URLs and thebackendmute-leg value are unchanged.
August 7, 2026
Added
- Webhook delivery URL. Applications could read and update their own
webhook delivery URL with
GET /api/v1/app/webhookandPUT /api/v1/app/webhook. Sendingnullor an empty string disabled delivery. These routes were replaced on August 27 byGET /api/v1/appandPATCH /api/v1/app. - Health endpoints. Unauthenticated
GET /healthandGET /livezare now part of the public contract. Use/healthfor dependency readiness and/livezonly for process liveness.
Changed
- Webhook delivery window. Webhook events more than five minutes old at delivery time go to the dead-letter queue without an HTTP attempt. After a prolonged endpoint outage or delivery backlog, reconcile current resource state instead of assuming every lifecycle event will arrive. See Webhook delivery.
- Session base path. The canonical API base URL is
https://api.telekesher.dev. Session endpoints moved from/api/v1/sessions...to/v1/sessions.... Remove the/apisegment from constructed Session request URLs.
August 6, 2026
Changed
- Session event names. Session webhook names now use the
sessionresource namespace:call.created,call.answered,call.ringing_started,call.early_media_started, andcall.endedbecame their correspondingsession.*events. Update event filters and webhook dispatch logic to use the new names. - Room Playback terminal events. Terminal room playback events
are now
room.playback.endedfor natural completion or an API stop, androom.playback.failedfor errors and timeouts. The formerroom.playback.finishedis no longer used. Inspect the event’sreasonto distinguish the terminal outcome. - Session lifecycle fields. Session lifecycle fields are endpoint-specific.
Items returned by
GET /sessionsusestate, whileGET /sessions/{session_uuid}usestatus. Both fields have the same lifecycle values. Applications that deserialize both responses into one model should accept the field used by each endpoint. - Shared rate-limit bucket.
playback/seek,recording/mask, andrecording/unmaskdraw from one sharedseekrate-limit bucket. A burst of one of these commands can reduce the capacity available to the other two. See Rate limits. - Idempotent command
503responses. A503after an idempotent command may mean the command completed but its saved response could not be returned. Retry once with the sameIdempotency-Key. If it remains unavailable, reconcile from the resulting resource or webhook events before issuing a new key. - WebSocket pause and resume. WebSocket pause and resume affect audio in both directions. Pausing preserves buffered backend audio, and resuming does not change mute state.
Fixed
- Zero-value playback seeks. Playback seek requests now reject
seek_ms: 0. Use a positive value to seek forward or a negative value to rewind. - Authentication error responses. Authentication and client-error
responses now distinguish malformed
credentials (
400), missing or invalid credentials (401), forbidden operations (403), and resources hidden by application ownership (404). Error responses expose a stablecodefor programmatic handling.