Overview
vaani_flutter_sdk dispatches a Vaani agent through the Vaani External API, then joins the room the agent was sent to over WebRTC. You give it an API key and an agent ID.
It is a Dart port of the WebRTC and dispatch parts of the Python SDK, with the same endpoints, field names and error types. Source: VaaniResearchLab/vaani-flutter-sdk.
Included: call state, live transcripts, mute, speaking indicators, echo cancellation and noise suppression, and typed errors.
Installation
The package is not on pub.dev yet. Add it as a git dependency:This package runs the live call in your app. Call history, transcripts, recordings, campaigns and agent settings are handled through the Vaani portal or your backend. See Call History, Transcripts and Agent Settings.
iOS
Add toios/Runner/Info.plist:
UIBackgroundModes: audio keeps the call running when the app goes to the background. Leave it out if the call should stop when the user leaves the app.
Android
Add toandroid/app/src/main/AndroidManifest.xml:
Authentication
Every request sends your API key as theX-API-Key header. Generate a key from platform.vaani.dev → Settings → API Keys. The key is shown only once.
Call
close() when you are done to free the HTTP connection. If you passed your own httpClient, close() leaves it open.
VaaniClient for a normal call: VaaniVoiceSession.withApiKey creates one and closes it in dispose().
Quickstart
VaaniVoiceSession is a ChangeNotifier, so it works with ListenableBuilder, Provider or Riverpod:
start() twice throws a StateError. Create a new VaaniVoiceSession for each conversation. The example app has a full call screen.
Voice Sessions
Creating a Session
VaaniVoiceSession.withApiKey(...) parameters:
The
VaaniVoiceSession(...) constructor takes credentialsProvider (required), roomOptions, enableMicrophone, audioOptions and agentJoinTimeout with the same defaults, plus:
Lifecycle
start() completes once the room is joined, not when the agent arrives. Watch session.state:
The session ends by itself when the agent leaves the room, when the room disconnects, or (session flow) when Vaani sends
session_ended. It moves to failed if no agent joins within agentJoinTimeout or Vaani sends agent_timeout.
Session Properties
isUserSpeaking and userAudioLevel are computed on the server, so they also prove the user’s audio is arriving. Use them to debug “the agent can’t hear me”.
Microphone
Live Transcripts
transcripts is a broadcast stream. Subscribe before start() to catch the agent’s greeting. If the agent sends each transcript over two channels, the SDK keeps one and drops the duplicate.
Using the Room Directly
session.room is the underlying WebRTC room. Use it for anything the session doesn’t wrap, such as audio tracks, RPC, data messages and custom events. If you add your own listeners to it, dispose them yourself; the session only cleans up its own.
Dispatch Flows
“Dispatch” means asking Vaani to send the agent into a room and getting back credentials to join it. There are two ways.- trigger-call (default)
- Session flow
One request:
POST /api/trigger-call/ with medium: "webrtc". Use it for per-call voice, language or prompt overrides. end() leaves the room.trigger-call Options
Fields left
null are not sent, so the server default applies. bgNoiseVolume and voiceSpeed outside their ranges fail an assert in debug builds and are rejected by the API with 422.
ModifyAgentConfig(persona:, training:, experience:, analysis:) takes one map per section, in the same shape as the agent configuration. Sections you leave out are unchanged, and the override applies to this call only.
Session Flow
1
Create a session
session/create returns a sessionToken and a shareable sessionUrl.2
Connect and get room credentials
session/connect exchanges the session token for the server URL, participant token and room name. Vaani dispatches the agent at this point.3
Join the room
The SDK joins the room, enables the mic and subscribes to session events.
4
End the session
end() calls session/disconnect. With explicitEnd: true (the default) the session ends now; with false it stays resumable for gracePeriod.VaaniVoiceSession.withApiKey(useSessionFlow: true, ...) runs all four steps:
withApiKey always uses ui_type: "fullpage". To set widget, build the provider yourself:
gracePeriod and sessionTtl outside their ranges are rejected with 422 (VaaniValidationException). Once a session has ended or expired, session calls fail with 410 (VaaniSessionExpiredException).Real-Time Session Events
In the session flow,session.sessionEvents streams events from the session WebSocket:
The session-events socket is public, like
connect and status: the session token in the URL is the credential. A wrong or ended token is refused, and the error arrives on sessionEvents. For live call transcripts use session.transcripts, not this socket.Keep the API Key Off the Device
withApiKey is fine for prototypes and internal builds. In production, keep the key on your backend: the backend dispatches the agent and returns only the room credentials.
Backend (Python, with the Python SDK):
VaaniCredentialsProvider and override release() so the session ends with the call:
release() runs once when the call ends, whether through end(), an error, the agent leaving or dispose(). Errors it throws are ignored, because Vaani expires sessions on its own.
VaaniRoomCredentials:
Echo Cancellation and Noise Suppression
VaaniVoiceSession sets the microphone’s voice processing explicitly through audioOptions, so a dependency upgrade cannot change it silently. By default everything is on.
VaaniAudioOptions.raw() turns everything off, for raw capture or your own audio pipeline.
The processing modes wrap an API that the WebRTC library marks experimental; the on/off flags are stable. Judge echo cancellation on a real device on speakerphone, since simulators and emulators cannot show it. On Android,
EnableBuiltInAEC(1) and EnableBuiltInNS(1) in logcat confirm the hardware effects are on.Lower-Level API
VaaniClient calls the dispatch and session endpoints directly, without joining a room.
Dispatch a WebRTC Call
triggerWebRtcCall and triggerTelephonyCall return a TriggerCallResponse: token, roomName, connectionUrl and liveCaptionsUrl for WebRTC; callId, roomName, liveCaptionsUrl, agentName, dispatchId and callDbId for telephony. The full response is in .raw. dispatchWebRtc throws a VaaniException if the response has no token, room name or URL.
Outbound Phone Calls
The same endpoint places phone calls. This usually belongs on a backend, but it is available:Session Endpoints
client.sessions.createAndConnect(...) takes the same parameters as create and returns VaaniRoomCredentials with sessionToken set.
WebRtcSessionStatus (from get and disconnect) has status, expiresAt, uiType, roomName, agentId, clientId, metadata and gracePeriodEndsAt.
Other Endpoints
client.request(method, path, body:, query:) sends an authenticated JSON request to any endpoint the SDK doesn’t wrap, with the same error mapping:
Error Handling
Every error extendsVaaniException, which carries message (usually the API’s detail) and statusCode.
start() throws on dispatch or connection failures. Failures after that (such as the agent timeout) don’t throw: state becomes failed and session.error holds the exception, so check it in your listener.
Troubleshooting
When you contact Vaani support, include
session.roomName (e.g. webrtc-1794481532-5effcd48). It identifies the call.
Call History, Transcripts and Agent Settings
The Flutter SDK runs the live call. Everything around the call is done in the Vaani portal or from your backend:
For example, your backend can fetch a call’s transcript and summary after the app reports its
roomName:
client.request(method, path, body:, query:) is available.
