> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upliftai.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 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}`](/api-reference/sessions-%26-call-records/get-a-sessions-status) instead.



## OpenAPI

````yaml /voice-agents/api-reference/openapi.yaml get /realtime-assistants/sessions/{sessionId}/detail
openapi: 3.1.0
info:
  title: UpliftAI Voice Agents API
  version: '1.0'
  description: >-
    Build voice assistants, start conversations on the web or the phone, run
    calling campaigns, and receive outcomes by webhook. All endpoints take a
    bearer API key unless noted otherwise.
servers:
  - url: https://api.upliftai.org/v1
security:
  - apiKey: []
tags:
  - name: Assistants
  - name: Authoring an assistant
  - name: Starting a conversation
  - name: Sessions & call records
  - name: Campaigns
  - name: Campaign outcomes
  - name: Contacts
  - name: Google Sheet sources
  - name: Integrations & delivery health
paths:
  /realtime-assistants/sessions/{sessionId}/detail:
    get:
      tags:
        - Sessions & call records
      summary: Get a call's transcript and outcomes
      description: >-
        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}`](/api-reference/sessions-%26-call-records/get-a-sessions-status)
        instead.
      operationId: getSessionDetail
      parameters:
        - name: sessionId
          in: path
          required: true
          description: >-
            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.
          schema:
            type: string
          example: 25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx
        - name: include_audio_url
          in: query
          required: false
          description: >-
            Adds `audioUrl` to the response. Must be exactly `true` or `false` —
            `1`, `TRUE` and a bare `?include_audio_url` are all 400s.
          schema:
            type: boolean
            default: false
          example: true
      responses:
        '200':
          description: The call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SessionDetail'
              examples:
                completedCall:
                  summary: A finished outbound call
                  value:
                    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'
                neverConnected:
                  summary: A web session that never connected
                  value:
                    sessionId: c9964204-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                    realtimeAssistantId: 452dda41-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                    channel: web
                    direction: inbound
                    state: created
                    connected: false
                    outcome: no_answer
                    roomName: shifa-clinic-de-c9964204-xxxx-xxxx-xxxx-xxxxxxxxxxxx
                    participantIdentity: عائشہ صدیقی
                    createdAt: '2026-08-18T05:14:08.525Z'
                    transcript: []
                    toolCalls: []
        '400':
          description: '`include_audio_url` was neither `true` nor `false`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: Validation failed (boolean string is expected)
                error: Bad Request
                statusCode: 400
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: No session with that id in the API key's project.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                message: 'Session not found: 00000000-0000-4000-8000-000000000000'
                error: Not Found
                statusCode: 404
      x-codeSamples:
        - lang: bash
          label: Detail with the recording, id encoded
          source: >
            # 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>'
        - lang: python
          label: Detail with the recording, id encoded
          source: >
            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())
        - lang: javascript
          label: Detail with the recording, id encoded
          source: >
            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))
        - lang: php
          label: Detail with the recording, id encoded
          source: >
            <?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);
        - lang: go
          label: Detail with the recording, id encoded
          source: |
            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))
            }
        - lang: java
          label: Detail with the recording, id encoded
          source: >
            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());
components:
  schemas:
    SessionDetail:
      type: object
      properties:
        sessionId:
          type: string
          format: uuid
        realtimeAssistantId:
          type: string
          format: uuid
        channel:
          type: string
          enum:
            - web
            - telephony
            - whatsapp
        direction:
          type: string
          enum:
            - inbound
            - outbound
          description: Web sessions are always `inbound` — the user opens them.
        state:
          type: string
          enum:
            - created
            - dispatched
            - dialing
            - ringing
            - answered
            - active
            - completed
            - failed
          description: >-
            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`.
        connected:
          type: boolean
          description: >-
            Whether the call ever reached a conversation. False for every
            dial-time state and for `failed`.
        outcome:
          type: string
          enum:
            - no_answer
            - busy
            - voicemail
            - unreachable
            - silent_pickup
            - failed
          description: >-
            Why the call never reached a conversation. Present only when
            `connected` is false.
        endedBy:
          type: string
          enum:
            - callee
            - agent
            - supervisor
            - system
          description: Who hung up.
        toNumber:
          type: string
          description: E.164. Telephony only.
          example: '+923001234567'
        fromNumber:
          type: string
          description: E.164 caller id the call was placed from. Telephony only.
          example: '+924232591000'
        calleeName:
          type: string
          description: Display name of the person called, from the campaign contact row.
        roomName:
          type: string
        participantIdentity:
          type: string
          description: >-
            The identity the client joined the room under — usually a person's
            name for web clients, a generated key for dialed calls.
        transportProvider:
          type: string
          example: twilio
        variables:
          type: object
          additionalProperties: true
          description: The values you passed when the call or session was created.
        assistantVersion:
          type: integer
          description: >-
            The assistant version this session ran. Absent on sessions from
            before versions.
        assistantAlias:
          type: string
          enum:
            - prod
            - draft
          description: >-
            The alias the session was launched on. Absent when it was launched
            by an exact version number, and on sessions from before versions.
        campaignId:
          type: string
          format: uuid
          description: Set on calls a campaign dialed.
        runId:
          type: string
          format: uuid
          description: >-
            The campaign run (one launch) this call belongs to. Set with
            `campaignId`.
        contactId:
          type: string
          description: The campaign contact this call was placed to.
        createdAt:
          type: string
          format: date-time
        dialingAt:
          type: string
          format: date-time
          description: >-
            Carrier-reported dial progress. A carrier that never reports a hop
            leaves this and `ringingAt` absent even on a call that connected.
        ringingAt:
          type: string
          format: date-time
        connectedAt:
          type: string
          format: date-time
          description: >-
            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.
        answeredAt:
          type: string
          format: date-time
          description: When the conversation started.
        endedAt:
          type: string
          format: date-time
        durationSec:
          type: integer
          description: >-
            Talk time, `answeredAt` → `endedAt`. Absent when either timestamp
            is.
        failureReason:
          type: string
          enum:
            - wrong_number
            - busy
            - declined
            - no_answer
            - unreachable
            - voicemail
            - silent_pickup
            - network_error
            - call_failed
          description: >-
            Why a `failed` call failed. Appears alongside `outcome: failed` and
            is the authoritative field of the two.
        failureReasonWithCode:
          type: string
          description: >-
            `failureReason` with the carrier's SIP code appended when there was
            one. Switch on `failureReason`; show this.
          example: busy:486
        transcript:
          type: array
          description: >-
            The conversation in order. Empty on calls that never connected, and
            on a connected call whose report has not been stored yet.
          items:
            $ref: '#/components/schemas/SessionTranscriptTurn'
        toolCalls:
          type: array
          description: Tools the assistant invoked during the call, in order.
          items:
            $ref: '#/components/schemas/SessionToolCall'
        grade:
          $ref: '#/components/schemas/CallScore'
        summary:
          $ref: '#/components/schemas/ConversationSummary'
        conversion:
          $ref: '#/components/schemas/SessionConversion'
        callback:
          $ref: '#/components/schemas/SessionCallback'
        audioUrl:
          type: string
          format: uri
          description: >-
            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.
    Error:
      type: object
      properties:
        message:
          description: >-
            One message per failed field for validation errors, a single string
            otherwise.
          oneOf:
            - type: string
            - type: array
              items:
                type: string
        error:
          type: string
        statusCode:
          type: integer
    SessionTranscriptTurn:
      type: object
      properties:
        speaker:
          type: string
          enum:
            - agent
            - caller
        text:
          type: string
        atSec:
          type: integer
          description: >-
            Seconds into the recording this turn began, for lining the
            transcript up with `audioUrl`. Absent when the report carries no
            recording start time.
    SessionToolCall:
      type: object
      properties:
        name:
          type: string
        arguments:
          type: object
          additionalProperties: true
          description: The arguments the model called the tool with.
        result:
          type: string
          description: >-
            What the tool returned, JSON-stringified. Absent when the tool
            returned nothing.
        callId:
          type: string
          description: Pairs the tool call with its result inside the agent's report.
        atSec:
          type: integer
          description: Seconds into the recording, as on transcript turns.
    CallScore:
      type: object
      description: >-
        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.
      properties:
        rubricVersionId:
          type: string
          description: The rubric version this call was graded against.
        score:
          type:
            - integer
            - 'null'
          description: >-
            Percent of the applicable points earned, 0 to 100. Rules marked N/A
            drop out, so 40 earned of 60 applicable is 67. `null` when no rule
            applied.
        criteria:
          type: array
          items:
            $ref: '#/components/schemas/CallScoreCriterion'
        sentiment:
          type: string
          enum:
            - happy
            - neutral
            - annoyed
          description: >-
            How the caller sounded. Absent on calls graded before this was
            added.
        gradedAt:
          type: string
          format: date-time
    ConversationSummary:
      type: object
      description: >-
        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.
      properties:
        headline:
          type: string
          description: One sentence, end state first.
          example: >-
            Farmer agreed to try one bottle of Cruiser on the next corn crop and
            asked for an expert callback.
        bullets:
          type: array
          description: Usually three to five, in call order. Events, not values.
          items:
            type: string
          example:
            - >-
              Agent confirmed the farmer and introduced Cruiser seed treatment
              for corn
            - Farmer agreed to try one bottle on the next sowing
            - >-
              Farmer asked about rice pollination temperature and wanted an
              expert to call
        facts:
          type: array
          description: >-
            One per distinct fact, in display order. The first is the person's
            Decision or the Next Step when one exists.
          items:
            $ref: '#/components/schemas/ConversationSummaryFact'
        language:
          type: string
          enum:
            - en
        promptVersion:
          type: string
          example: call-summary-v1
        model:
          type: string
          description: The model that wrote it.
          example: gpt-5.4-2026-03-05
        generatedAt:
          type: string
          format: date-time
      required:
        - headline
        - bullets
        - facts
        - language
        - promptVersion
        - model
        - generatedAt
    SessionConversion:
      type: object
      description: >-
        The goal outcome recorded for this call. Absent when the call produced
        none.
      properties:
        goalKind:
          type: string
          enum:
            - appointment
            - event
            - lead
            - order_confirmation
            - payment_reminder
            - outreach
            - reservation_confirmation
        payload:
          $ref: '#/components/schemas/ConversionPayload'
        slotIso:
          type: string
          format: date-time
          description: The time that was booked, for goals that book one.
        createdAt:
          type: string
          format: date-time
    SessionCallback:
      type: object
      description: A request to have a human call this person back.
      properties:
        reason:
          type: string
          description: Why, in the grader's words.
        status:
          type: string
          enum:
            - open
            - done
        callbackAfter:
          type: string
          format: date-time
          description: Don't call before this.
        createdAt:
          type: string
          format: date-time
    CallScoreCriterion:
      type: object
      properties:
        ruleId:
          type: string
        title:
          type: string
        points:
          type: integer
          description: The most this rule could earn.
        earned:
          type: integer
          description: '`points` on a pass, 0 on a fail or N/A.'
        passed:
          type:
            - boolean
            - 'null'
          description: >-
            `null` when the rule did not apply to this call. Read `status` for
            the three-way answer.
        status:
          type: string
          enum:
            - pass
            - fail
            - not_applicable
          description: Absent on calls graded before N/A existed.
        reason:
          type: string
          maxLength: 500
          description: >-
            Why the grader answered this way. Always present on
            `not_applicable`.
        evidence:
          type: object
          description: The moment in the call the answer rests on. Can be absent.
          properties:
            atSec:
              type: integer
              description: Seconds into the recording, as on transcript turns.
            quote:
              type: string
              maxLength: 500
              description: Copied from the transcript.
            speaker:
              type: string
              enum:
                - agent
                - contact
    ConversationSummaryFact:
      type: object
      properties:
        label:
          type: string
          description: >-
            One to three words, Title Case, e.g. `Decision`, `Delivery Address`,
            `Complaint Ref`.
          example: Decision
        value:
          type: string
          description: Up to fifteen words, English, Latin script.
          example: Will try one bottle on the next corn crop
      required:
        - label
        - value
    ConversionPayload:
      description: >-
        What the agent got out of the call. `kind` tracks the campaign's
        `goalKind`, except that an `event` campaign records `rsvp` payloads.
        Every field but `kind` is best-effort — the agent omits whatever the
        caller never said.
      oneOf:
        - title: Appointment
          type: object
          properties:
            kind:
              type: string
              const: appointment
            outcome:
              type: string
            summary:
              type: string
        - title: RSVP
          type: object
          properties:
            kind:
              type: string
              const: rsvp
            outcome:
              type: string
              enum:
                - confirmed
                - maybe
            party:
              type: integer
              minimum: 1
              description: Headcount the caller is bringing, including themselves.
            summary:
              type: string
        - title: Lead
          type: object
          properties:
            kind:
              type: string
              const: lead
            outcome:
              type: string
            status:
              type: string
              enum:
                - qualified
                - callback
                - not_interested
                - wrong_person
                - needs_human
            interest:
              type: string
            nextStep:
              type: string
            notes:
              type: string
            summary:
              type: string
        - title: Order confirmation
          type: object
          properties:
            kind:
              type: string
              const: order_confirmation
            status:
              type: string
              enum:
                - confirmed
                - cancelled
                - amended
                - address_incomplete
                - wrong_person
                - callback
            address:
              type: string
              description: Delivery address as the caller gave or corrected it.
            orderId:
              type: string
            orderValue:
              type: object
              properties:
                amount:
                  type: number
                currency:
                  type: string
            summary:
              type: string
        - title: Payment reminder
          type: object
          properties:
            kind:
              type: string
              const: payment_reminder
            status:
              type: string
              enum:
                - will_pay
                - already_paid
                - dispute
                - callback
                - wrong_person
            promiseDate:
              type: string
              description: The date the caller committed to pay by.
            summary:
              type: string
        - title: Outreach
          type: object
          properties:
            kind:
              type: string
              const: outreach
            status:
              type: string
              enum:
                - interested
                - callback
                - not_interested
                - wrong_person
            summary:
              type: string
        - title: Reservation confirmation
          type: object
          properties:
            kind:
              type: string
              const: reservation_confirmation
            status:
              type: string
              enum:
                - confirmed
                - cancelled
                - rescheduled
                - wrong_person
                - callback
            party:
              type: integer
              minimum: 1
            reservationTime:
              type: string
            summary:
              type: string
  responses:
    Unauthorized:
      description: A bearer key was sent but is not recognized.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: invalid authorization
            error: Unauthorized
            statusCode: 401
    Forbidden:
      description: No API key sent, or the key lacks permission for this resource.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Forbidden resource
            error: Forbidden
            statusCode: 403
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: Project API key (`sk_api_…`).

````