AI Voice (Call)
Real-time AI voice astrologer over WebSocket. Audio streams in both directions — sub-second turn-taking, barge-in, multilingual, chart-grounded. Live transcripts stream alongside the audio for both the user and the astrologer, so you can render captions or build a chat log in parallel. Integration has two phases: one REST call to create the session, one WebSocket for the live conversation.
How it works
- Phase 1 (REST) — POST birth data to
/start(or/match-startfor two charts). Server computes the Vedic chart, returns asession_id. - Phase 2 (WebSocket) — connect to
wss://starsapi.com/voice-relay-v2, authenticate with thesession_id, then exchange audio frames in real time.
The relay handles voice activity detection (VAD), turn-taking, barge-in, language detection, and billing events. Your client captures mic audio and plays back the astrologer’s audio — nothing else to manage.
Phase 1: Create session
POST /api/v3/partner/ai/voice/start
Creates a voice session and computes the birth chart. Returns
session_id to use in the WebSocket auth frame.
Same birth-data fields as chat /start.
Available voice astrologers — single chart: transit_expert, love_guru,
transit_expert.
Two-chart sessions
POST /api/v3/partner/ai/voice/match-start
For compatibility voice calls. Same person1 / person2
body as chat /match-start.
Returns a session_id with both charts loaded. Use
person1_name and person2_name in the WebSocket
greet frame instead of name.
Phase 2: WebSocket conversation
wss://starsapi.com/voice-relay-v2
Connection flow
- Connect to the relay URL.
- Send an
authframe withsession_id,api_key,name, andlang. - Wait for
ready— session is bound, astrologer loaded. - Send
greetto trigger the astrologer’s opening response. - Stream mic audio as
audio.chunkframes. Play backassistant.audioframes.
Audio format
Same format in both directions: 24 kHz, mono, signed 16-bit little-endian PCM,
base64-encoded in the JSON data field.
Client requirements
The relay handles VAD, turn-taking and billing. Your client is responsible for audio capture, playback and session handling — the pieces below are what every integration needs, whatever the platform.
| Area | Requirement |
|---|---|
| Mic permission | Android RECORD_AUDIO; iOS NSMicrophoneUsageDescription; web getUserMedia. Request it before creating the session, not mid-call. |
| Audio session | iOS: AVAudioSession category playAndRecord with defaultToSpeaker. Android: capture with the VOICE_COMMUNICATION source and request audio focus. Release it on every exit path. |
| Echo cancellation | Must be ON — the platform sources above enable it. Barge-in detection depends on it: without AEC the astrologer’s own voice re-enters the mic and interrupts itself. |
| Capture | 24 kHz mono PCM s16le, streamed in ~100–200 ms chunks, base64-encoded per audio.chunk frame. Do not resample on the server side — send 24 kHz. |
| Playback | A raw-PCM streaming player that accepts scheduled 24 kHz PCM16 buffers — Web Audio AudioBuffer, Android AudioTrack, iOS AVAudioEngine, or Flutter flutter_pcm_sound. Ordinary file/URL players cannot stream raw chunks. Plan this piece first — it is the only non-trivial dependency in the integration. |
| Playback scheduling | Schedule each chunk at an absolute timeline position (nextStart = max(now, nextStart); play(chunk, at: nextStart); nextStart += chunk.duration). Waiting for the previous chunk to finish produces audible gaps and clicks. |
| WebSocket | A client that keeps the connection open for the whole call. Frames are single-line JSON text — no binary frames, no compression required. |
| Web: secure context | Microphone access requires HTTPS (or localhost). On plain HTTP getUserMedia is unavailable and the call cannot start. |
| Web: capture | getUserMedia({ audio: { sampleRate: 24000, channelCount: 1, echoCancellation: true, noiseSuppression: true } }), then an AudioContext({ sampleRate: 24000 }). Use an AudioWorklet for the capture node — createScriptProcessor is deprecated and glitches under load. |
| Web: autoplay policy | Browsers create the AudioContext in a suspended state. Call audioContext.resume() inside the click handler that starts the call, otherwise frames arrive and nothing is heard. |
| During the call | Keep the screen awake, default the route to speaker, and follow the OS for wired/Bluetooth headsets. Test both. |
Frame reference
All frames are single-line JSON objects with a type field.
Client → Server
| Type | Fields | Description |
|---|---|---|
auth | session_id, api_key, name, lang | First frame. Binds the session to the WebSocket. |
greet | name, lang | Triggers the astrologer’s opening response. For couples: person1_name, person2_name, lang. |
audio.chunk | data | One mic chunk. Base64 PCM (24 kHz mono s16le). Send continuously while user speaks. |
interrupt | — | Cancel the current assistant response. Send when user.speech_started arrives while playback is active. |
Server → Client
| Type | Fields | Description |
|---|---|---|
ready | astrologer, language | Auth accepted, session bound. |
user.speech_started | — | Server VAD detected the user is speaking. |
user.speech_stopped | — | VAD detected end of user turn. |
user.transcript | text | Transcript of what the user said. |
assistant.speech_started | — | Astrologer is about to speak. Show speaking indicator. |
assistant.audio | data | One chunk of astrologer audio. Base64 PCM. Queue and play in order. |
assistant.transcript | delta | Incremental caption text for the current astrologer turn. |
assistant.done | — | Astrologer finished speaking. Open mic for next turn. |
assistant.cancelled | — | Barge-in acknowledged. Previous response stopped. |
billing.warning | — | Wallet running low. Show a top-up nudge. |
billing.exhausted | — | Wallet empty. Session ending — show top-up flow. |
error | code, message | Recoverable or fatal error. |
Barge-in
When user.speech_started arrives while you’re still playing
assistant audio, stop playback immediately and send {"type":"interrupt"}.
The relay replies with assistant.cancelled and starts processing
the new user turn.
Billing
Voice sessions use a per-minute meter. An initial charge is
debited when the connection is established, then recurring charges every
60 seconds while the session is active. Disconnecting stops the meter.
The relay sends billing.warning when balance is getting low and
billing.exhausted when the wallet is empty. No webhook wiring
needed — handle them inline as WebSocket frames.
Integration checklist
- POST to
/start(or/match-start) with birth data → savesession_id. - Connect to
wss://starsapi.com/voice-relay-v2. - Send
authframe → wait forready. - Send
greet→ playassistant.audiochunks, renderassistant.transcriptdeltas. - Wait for
assistant.donebefore opening mic (first turn only). - Capture mic at 24 kHz mono s16le, base64-encode, send as
audio.chunkframes. - On
user.speech_startedduring playback → stop audio, sendinterrupt. - Handle
billing.warning/billing.exhaustedto manage wallet state.
Errors
REST errors follow the same format as chat errors.
WebSocket errors arrive as {"type":"error","code":"...","message":"..."} frames.
| HTTP / WS | Code | Cause |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing/invalid fields or astrologer not available for voice. |
| 400 | MISSING_FIELD | A required field is absent. |
| 400 | INVALID_SESSION | Session ID not found or expired. |
| 401 | AUTH_MISSING_KEY | No API key in request or auth frame. |
| 402 | INSUFFICIENT_BALANCE | Wallet balance too low to start. |
| WS | billing.exhausted | Wallet empty during session — connection closing. |
| WS | error | Fatal or recoverable error with code and message. |
See also
- AI Astrologers overview — available personas, billing, language support
- AI Chat (Text) — text-based chat endpoints
- Authentication — keys, headers, origin whitelisting