Guides
Mobile apps
Put a voice call with your agent, and a chat beside it, in your own iOS or Android app.
The mobile SDKs do in an app what @aigently/web does in a page (Browser calls):
your server starts a call and your app joins it, and a chat runs on the agent's own widget session.
The calls, the states and the rules are the same.
Start the call on your server
#!/bin/sh
# Start a browser call for your page. From your server, with a live key: give the page the url and token, and it joins with @aigently/web.
curl -sS --fail-with-body -X POST "https://api.aigently.ai/v1/web-calls" \
-H "Authorization: Bearer $AIGENTLY_API_KEY" \
-H "Idempotency-Key: visit-20931" \
--json '{
"agent_id": "3c7d9e1f-5a2b-4c6d-8e0f-1a3b5c7d9e2f",
"variables": {
"first_name": "Sara"
},
"metadata": {
"session": "S-20931"
}
}'
This is the same request as for a browser call. It needs calls:write and a live key, which
stays on your server: never put an API key in an app. The answer's url and token go to the app,
and the token joins this one call for 15 minutes.
Join it from an iOS app
import Aigently
// Your own endpoint, which calls POST /v1/web-calls and returns url and token.
let credentials = try await yourServer.startCall()
let call = try await joinCall(credentials, handlers: CallHandlers(
onState: { state in print(state) },
onTranscript: { line in print(line.speaker, line.text) }
))
It needs iOS 13 or macOS 10.15, and Xcode 16.3 or later. Add NSMicrophoneUsageDescription to your
Info.plist: the first call asks for the microphone, and a person who refuses gets a call that ends
at once. For SwiftUI, WebCallModel holds a call as state and hangs up when it goes away.
Join it from an Android app
import ai.aigently.*
// Your own endpoint, which calls POST /v1/web-calls and returns url and token.
val credentials = yourServer.startCall()
val call = joinCall(context, credentials, CallHandlers(
onState = { state -> render(state) },
onTranscript = { line -> show(line) },
))
It needs Android 5.0 (API 21) or later. Ask for RECORD_AUDIO before you join: a library cannot
show the permission prompt, and a call joined without it ends at once. For Compose,
WebCallController holds a call as state.
What the call tells you
| State | What it means |
|---|---|
| connecting | Joining the call. |
| waiting | Joined; the agent is getting ready. |
| listening | The agent is listening. |
| thinking | The agent is working out what to say. |
| speaking | The agent is talking. |
| ended | The call is over. |
Each transcript line has a speaker, you or the agent, and grows while it is spoken, then arrives
once more as final. Keep the latest copy of each line by its id: Transcript does this on both
platforms. The agent's audio plays by itself.
A chat
let chat = try await startChat(ChatOptions(
agentId: "8a1f3c2e-…",
publishableKey: "pk_…",
origin: "https://www.aigently.ai"
))
let answer = try await chat.send("When do you open?")
val chat = startChat(ChatOptions(
agentId = "8a1f3c2e-…",
publishableKey = "pk_…",
origin = "https://www.aigently.ai",
))
val answer = chat.send("When do you open?")
A chat uses the agent's publishable key, the one in your widget snippet. An app names the site
it speaks for: the platform opens a chat only for a domain listed under Allow your domains on
the agent's Connect tab, and it reads that domain from the request's Origin header, which a
browser sends and an app does not. Pass one of the listed domains, usually your own website's, as
origin. Values a person could type go in variables, as text. Values your server vouches for go in
a signed identity token, as for the widget.
Getting the SDKs
The mobile SDKs will be published with our other SDKs. Until then, a call works with any LiveKit
client: join url with token, publish the microphone, and play the agent's audio.