Get a session transcript
GET /api/billing/sessions/{session_id}/Returns the full AgentSessionSummary for a single voice session, including the entire conversation transcript.
| Auth | JWT bearer or X-API-Key (see API Keys) |
| Scoping | Staff see any session. All other callers see only sessions for AI characters they created. |
| Path param | session_id (UUID) returned at session start |
# Using an API key (recommended for backends)
curl https://api.oshara.ai/api/billing/sessions/sess_a1b2c3/ \
-H "X-API-Key: sk_..."
# Or using a JWT
curl https://api.oshara.ai/api/billing/sessions/sess_a1b2c3/ \
-H "Authorization: Bearer <token>"Response
Wrapped in the standard envelope ({ success, message, data, errors }). The data field contains:
{
"id": 142,
"session_id": "sess_a1b2c3",
"tenant_id": "acme",
"character_slug": "support-bot",
"user": 7,
"room_name": "oshara-voice-support-bot-user123-abc12",
"origin_url": "https://yoursite.com/contact",
"metadata": { "user_id": "u_42", "plan": "pro" },
"session_duration_seconds": 184.7,
"turn_count": 14,
"interruption_count": 2,
"llm_input_tokens": 1840,
"llm_output_tokens": 612,
"llm_model": "gpt-4o-mini",
"llm_provider": "openai",
"stt_audio_seconds": 92.3,
"stt_provider": "openai",
"tts_characters": 1284,
"tts_provider": "chatterbox",
"estimated_cost_usd": "0.014200",
"finalized_at": "2026-05-28T14:23:48Z",
"recording_status": "READY",
"recording_url": "s3://oshara-recordings/sess_a1b2c3.mp4",
"recording_presigned_url": "https://s3.amazonaws.com/...?X-Amz-Signature=...",
"transcript": [
{ "role": "assistant", "text": "Hi! How can I help you today?", "turn_index": 0, "ts": 0.42 },
{ "role": "user", "text": "I want to book a demo.", "turn_index": 1, "ts": 3.18 },
{ "role": "assistant", "text": "Great — what day works for you?","turn_index": 2, "ts": 5.04 }
],
"transcript_text": "Assistant: Hi! How can I help you today?\nUser: I want to book a demo.\nAssistant: Great — what day works for you?",
"created_at": "2026-05-28T14:20:43Z"
}Transcript shape
transcript is an ordered array — index 0 is the first utterance, increasing over time.
| Field | Type | Description |
|---|---|---|
role | string | "user" or "assistant". Tool calls / system messages are excluded. |
text | string | The exact text — STT output for user turns, LLM output for assistant turns. |
turn_index | number | 0-based turn counter within the session. |
ts | number | Seconds since the session started. Useful for syncing to the recording. |
transcript_text is the same data flattened into a single string — convenient for previews, search indexing, or feeding back into an LLM as context.
Field reference
| Field | Description |
|---|---|
session_id | The same UUID returned from POST /api/agents/agent-session/. |
tenant_id / character_slug | Which tenant and character this session belonged to. |
user | User ID that owned the session (if authenticated). |
room_name | LiveKit room name — useful for correlating with LiveKit dashboard logs. |
origin_url | The page URL that initiated the call. |
metadata | Whatever you passed at session start (user_id, plan, etc.). |
session_duration_seconds | Wall-clock length of the call. |
turn_count | Total turns (user + assistant). |
interruption_count | Times the user spoke over the agent. |
llm_*, stt_*, tts_* | Token / audio / character usage for cost accounting. |
estimated_cost_usd | Sum of LLM + STT + TTS costs based on provider unit prices. |
recording_url | S3 URI of the call recording (private). |
recording_presigned_url | Time-limited HTTPS URL that anyone can play. Refresh by re-fetching this endpoint. |
finalized_at | When the worker finalized the session (post-call). null for in-progress sessions. |
Errors
| Status | Cause |
|---|---|
400 Bad Request | session_id is not a valid UUID |
401 Unauthorized | Missing / expired JWT, or invalid / inactive X-API-Key |
404 Not Found | Session doesn’t exist or the caller doesn’t own the character that ran the session. Treated the same to avoid leaking session IDs across tenants. |
Common patterns
Get transcript only
const r = await fetch(`https://api.oshara.ai/api/billing/sessions/${id}/`, {
headers: { "X-API-Key": process.env.OSHARA_API_KEY }
}).then(r => r.json());
const turns = r.data.transcript; // array of { role, text, turn_index, ts }Stream the recording while showing the transcript
const { recording_presigned_url, transcript } = (await fetchSession(id)).data;
audio.src = recording_presigned_url;
audio.addEventListener("timeupdate", () => {
const active = transcript.findLast(t => t.ts <= audio.currentTime);
highlight(active?.turn_index);
});Index transcripts into your own search system
const sessions = (await listSessions({ character_slug: "support-bot" })).data.sessions.data;
for (const s of sessions) {
await elastic.index({
index: "voice-transcripts",
id: s.session_id,
body: {
slug: s.character_slug,
created_at: s.created_at,
transcript: s.transcript_text,
duration: s.session_duration_seconds,
}
});
}Related
- List sessions for a character → Session History
- Mint the
session_idin the first place → Sessions - Form submissions tied to a session → Form Responses
Last updated on