# The id is percent-encoded: # becomes %23, + becomes %2B
curl 'https://api.upliftai.org/v1/realtime-assistants/sessions/7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx%23%2B923001234567%23a2/detail?include_audio_url=true' \
-H 'Authorization: Bearer <token>'import requests
from urllib.parse import quote
# Campaign call ids carry # and +, so always encode the id in the path
session_id = "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2"
url = f"https://api.upliftai.org/v1/realtime-assistants/sessions/{quote(session_id, safe='')}/detail"
response = requests.get(url, headers={"Authorization": "Bearer <token>"}, params={"include_audio_url": "true"})
print(response.json())
const options = { method: 'GET', headers: { Authorization: 'Bearer <token>' } }
// Campaign call ids carry # and +, so always encode the id in the path
const sessionId = '7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2'
fetch(`https://api.upliftai.org/v1/realtime-assistants/sessions/${encodeURIComponent(sessionId)}/detail?include_audio_url=true`, options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err))
<?php
// Campaign call ids carry # and +, so always encode the id in the path
$sessionId = '7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2';
$url = 'https://api.upliftai.org/v1/realtime-assistants/sessions/' . rawurlencode($sessionId) . '/detail?include_audio_url=true';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer <token>']);
echo curl_exec($ch);
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Campaign call ids carry # and +, so always encode the id in the path
sessionID := "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2"
endpoint := "https://api.upliftai.org/v1/realtime-assistants/sessions/" + url.PathEscape(sessionID) + "/detail?include_audio_url=true"
req, _ := http.NewRequest("GET", endpoint, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
// Campaign call ids carry # and +, so always encode the id in the path
String sessionId = "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2";
String url = "https://api.upliftai.org/v1/realtime-assistants/sessions/"
+ URLEncoder.encode(sessionId, StandardCharsets.UTF_8) + "/detail?include_audio_url=true";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Authorization", "Bearer <token>")
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
require 'uri'
require 'net/http'
url = URI("https://api.upliftai.org/v1/realtime-assistants/sessions/{sessionId}/detail")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"sessionId": "25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"realtimeAssistantId": "452dda41-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"channel": "telephony",
"direction": "outbound",
"state": "completed",
"connected": true,
"toNumber": "+923001234567",
"fromNumber": "+924232591000",
"roomName": "call-25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"participantIdentity": "callee-25661352",
"transportProvider": "twilio",
"variables": {
"source": "assistant-test"
},
"createdAt": "2026-08-16T18:55:22.677Z",
"ringingAt": "2026-08-16T18:55:30.758Z",
"connectedAt": "2026-08-16T18:55:36.927Z",
"answeredAt": "2026-08-16T18:55:37.641Z",
"endedAt": "2026-08-16T18:56:17.411Z",
"durationSec": 40,
"transcript": [
{
"speaker": "caller",
"text": "ہیلو،",
"atSec": 1
},
{
"speaker": "agent",
"text": "ہیلو، میں ڈاکٹر عامرہ ہوں۔ میں خوابوں کی تعبیر اور ان کے پوشیدہ معنی سمجھنے میں آپ کی مدد کروں گی۔ آج کون سا خواب آپ کو لے کر آیا ہے؟",
"atSec": 6
},
{
"speaker": "caller",
"text": "ٹھیک ہے۔",
"atSec": 7
},
{
"speaker": "caller",
"text": "I was having a good dream. Do you know what a good dream is?",
"atSec": 18
},
{
"speaker": "agent",
"text": "جی ہاں، بالکل۔ اچھا خواب یا خوشگوار خواب وہ ہوتا ہے جس میں انسان کو سکون، خوشی، راحت یا امید محسوس ہو۔",
"atSec": 25
}
],
"toolCalls": [],
"summary": {
"headline": "Caller asked what a good dream means, got an explanation, and did not book a session.",
"bullets": [
"Caller opened by saying they had a good dream",
"Agent explained that a good dream leaves peace, happiness or hope",
"Call ended after the explanation with nothing arranged"
],
"facts": [
{
"label": "Next Step",
"value": "None"
},
{
"label": "Topic",
"value": "Meaning of a good dream"
}
],
"language": "en",
"promptVersion": "call-summary-v1",
"model": "gpt-5.4-2026-03-05",
"generatedAt": "2026-08-16T18:56:19.104Z"
}
}Get a call's transcript and outcomes
Read this after a call ends. The transcript comes from the post-call report, so it is empty on calls that never connected and briefly empty on one that just hung up; grade, conversion and callback come from that same pipeline, so treat all three as optional. While a call is still live, poll /realtime-assistants/sessions/{sessionId} instead.
# The id is percent-encoded: # becomes %23, + becomes %2B
curl 'https://api.upliftai.org/v1/realtime-assistants/sessions/7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx%23%2B923001234567%23a2/detail?include_audio_url=true' \
-H 'Authorization: Bearer <token>'import requests
from urllib.parse import quote
# Campaign call ids carry # and +, so always encode the id in the path
session_id = "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2"
url = f"https://api.upliftai.org/v1/realtime-assistants/sessions/{quote(session_id, safe='')}/detail"
response = requests.get(url, headers={"Authorization": "Bearer <token>"}, params={"include_audio_url": "true"})
print(response.json())
const options = { method: 'GET', headers: { Authorization: 'Bearer <token>' } }
// Campaign call ids carry # and +, so always encode the id in the path
const sessionId = '7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2'
fetch(`https://api.upliftai.org/v1/realtime-assistants/sessions/${encodeURIComponent(sessionId)}/detail?include_audio_url=true`, options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err))
<?php
// Campaign call ids carry # and +, so always encode the id in the path
$sessionId = '7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2';
$url = 'https://api.upliftai.org/v1/realtime-assistants/sessions/' . rawurlencode($sessionId) . '/detail?include_audio_url=true';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer <token>']);
echo curl_exec($ch);
package main
import (
"fmt"
"io"
"net/http"
"net/url"
)
func main() {
// Campaign call ids carry # and +, so always encode the id in the path
sessionID := "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2"
endpoint := "https://api.upliftai.org/v1/realtime-assistants/sessions/" + url.PathEscape(sessionID) + "/detail?include_audio_url=true"
req, _ := http.NewRequest("GET", endpoint, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
// Campaign call ids carry # and +, so always encode the id in the path
String sessionId = "7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2";
String url = "https://api.upliftai.org/v1/realtime-assistants/sessions/"
+ URLEncoder.encode(sessionId, StandardCharsets.UTF_8) + "/detail?include_audio_url=true";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Authorization", "Bearer <token>")
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
require 'uri'
require 'net/http'
url = URI("https://api.upliftai.org/v1/realtime-assistants/sessions/{sessionId}/detail")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"sessionId": "25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"realtimeAssistantId": "452dda41-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"channel": "telephony",
"direction": "outbound",
"state": "completed",
"connected": true,
"toNumber": "+923001234567",
"fromNumber": "+924232591000",
"roomName": "call-25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"participantIdentity": "callee-25661352",
"transportProvider": "twilio",
"variables": {
"source": "assistant-test"
},
"createdAt": "2026-08-16T18:55:22.677Z",
"ringingAt": "2026-08-16T18:55:30.758Z",
"connectedAt": "2026-08-16T18:55:36.927Z",
"answeredAt": "2026-08-16T18:55:37.641Z",
"endedAt": "2026-08-16T18:56:17.411Z",
"durationSec": 40,
"transcript": [
{
"speaker": "caller",
"text": "ہیلو،",
"atSec": 1
},
{
"speaker": "agent",
"text": "ہیلو، میں ڈاکٹر عامرہ ہوں۔ میں خوابوں کی تعبیر اور ان کے پوشیدہ معنی سمجھنے میں آپ کی مدد کروں گی۔ آج کون سا خواب آپ کو لے کر آیا ہے؟",
"atSec": 6
},
{
"speaker": "caller",
"text": "ٹھیک ہے۔",
"atSec": 7
},
{
"speaker": "caller",
"text": "I was having a good dream. Do you know what a good dream is?",
"atSec": 18
},
{
"speaker": "agent",
"text": "جی ہاں، بالکل۔ اچھا خواب یا خوشگوار خواب وہ ہوتا ہے جس میں انسان کو سکون، خوشی، راحت یا امید محسوس ہو۔",
"atSec": 25
}
],
"toolCalls": [],
"summary": {
"headline": "Caller asked what a good dream means, got an explanation, and did not book a session.",
"bullets": [
"Caller opened by saying they had a good dream",
"Agent explained that a good dream leaves peace, happiness or hope",
"Call ended after the explanation with nothing arranged"
],
"facts": [
{
"label": "Next Step",
"value": "None"
},
{
"label": "Topic",
"value": "Meaning of a good dream"
}
],
"language": "en",
"promptVersion": "call-summary-v1",
"model": "gpt-5.4-2026-03-05",
"generatedAt": "2026-08-16T18:56:19.104Z"
}
}Authorizations
Project API key (sk_api_…).
Path Parameters
Returned as sessionId by the call dispatch, or as callId on a campaign's calls list, like 7b2e91c4-xxxx-xxxx-xxxx-xxxxxxxxxxxx#+923001234567#a2. Always percent-encode it in the path, encodeURIComponent(sessionId) in JavaScript. Campaign call ids embed # and +, and an unencoded # truncates the id.
Query Parameters
Adds audioUrl to the response. Must be exactly true or false — 1, TRUE and a bare ?include_audio_url are all 400s.
Response
The call.
web, telephony, whatsapp Web sessions are always inbound — the user opens them.
inbound, outbound Web runs created → active → completed. Outbound telephony runs dispatched → dialing → ringing → answered → completed or failed; a carrier that reports no dial progress skips straight to answered.
created, dispatched, dialing, ringing, answered, active, completed, failed Whether the call ever reached a conversation. False for every dial-time state and for failed.
Why the call never reached a conversation. Present only when connected is false.
no_answer, busy, voicemail, unreachable, silent_pickup, failed Who hung up.
callee, agent, supervisor, system E.164. Telephony only.
"+923001234567"
E.164 caller id the call was placed from. Telephony only.
"+924232591000"
Display name of the person called, from the campaign contact row.
The identity the client joined the room under — usually a person's name for web clients, a generated key for dialed calls.
"twilio"
The values you passed when the call or session was created.
The assistant version this session ran. Absent on sessions from before versions.
The alias the session was launched on. Absent when it was launched by an exact version number, and on sessions from before versions.
prod, draft Set on calls a campaign dialed.
The campaign run (one launch) this call belongs to. Set with campaignId.
The campaign contact this call was placed to.
Carrier-reported dial progress. A carrier that never reports a hop leaves this and ringingAt absent even on a call that connected.
When the carrier bridged the audio. answeredAt is speech-gated, so connectedAt without it is a phantom answer: the line picked up and nobody spoke.
When the conversation started.
Talk time, answeredAt → endedAt. Absent when either timestamp is.
Why a failed call failed. Appears alongside outcome: failed and is the authoritative field of the two.
wrong_number, busy, declined, no_answer, unreachable, voicemail, silent_pickup, network_error, call_failed failureReason with the carrier's SIP code appended when there was one. Switch on failureReason; show this.
"busy:486"
The conversation in order. Empty on calls that never connected, and on a connected call whose report has not been stored yet.
Show child attributes
Show child attributes
Tools the assistant invoked during the call, in order.
Show child attributes
Show child attributes
The rubric grade, written by the post-call pipeline. Criterion titles and points are resolved into it, so rendering a scorecard needs no rubric lookup.
Show child attributes
Show child attributes
Call summary: how it went, in a headline and a few bullets, plus the facts that came up. In English. Not available for adhoc sessions.
Show child attributes
Show child attributes
The goal outcome recorded for this call. Absent when the call produced none.
Show child attributes
Show child attributes
A request to have a human call this person back.
Show child attributes
Show child attributes
Presigned link to the recording, valid for one hour. Minted without checking the recording exists, so a call that was never recorded still returns a link that 404s when fetched.
