---
title: "Get a single call by ID (the call record: details, recording, transcript, AI analysis)."
method: GET
path: "/api/v1/phones/calls/{id}"
tags: ["Phones - Calls"]
---

# Get a single call by ID (the call record: details, recording, transcript, AI analysis).

`GET /api/v1/phones/calls/{id}`

## Request

- Base URL: `https://api.voicelogica.ai`
- URL: `https://api.voicelogica.ai/api/v1/phones/calls/{id}`
- Auth: API key in header `x-api-key`

Retrieve complete information about a specific call including recordings, transcriptions, AI analysis, and all metadata.

**Response Includes:**
- Call status and timestamps (start, end, duration)
- Phone numbers (source and destination)
- Agent or user who handled the call
- Recording URL and transcription text
- AI-generated summary and analysis
- Dynamic variables used during the call
- Call direction (inbound/outbound)
- Campaign information (if applicable)
- Transfer history (if call was transferred)
- Retry information (for failed calls)

**Use Cases:**
- Review call details and conversation flow
- Access call recording and transcription
- Analyze AI agent performance
- Audit call handling for quality assurance
- Debug failed or problematic calls

## Path parameters

- `id` string, required

## Headers

- `x-api-key` string, required
- `x-company-id` string

## Response `200`

Call retrieved successfully

- object: Response schema for call including company metadata
  - `id` string, required: Unique identifier
  - `createdAt` string, date-time, required: Creation timestamp
  - `active` boolean, required: Whether the entity is active
  - `createdBy` string, required: Name of the user who created the entity
  - `createdById` string, required: ID of the user who created the entity
  - `modifiedAt` string, date-time: ISO 8601 date string
  - `modifiedBy` string: Name of the user who last modified the entity
  - `modifiedById` string: ID of the user who last modified the entity
  - `companyId` string, required: Company identifier
  - `company` object: Any valid JSON value
  - `groupIds` string[]: Linked group IDs
  - `callId` string, required: Call identifier
  - `callerId` string, required: Caller identifier
  - `contactId` string: Contact identifier
  - `userId` string: User identifier
  - `userName` string: Display name of userId — stamped on list responses so grids need no second lookup
  - `voipPhoneId` string: VoIP phone identifier
  - `sipTrunkId` string: SIP trunk identifier (for trunk-routed calls)
  - `agentId` string: Agent identifier
  - `agentVersionId` string: Agent version/staging snapshot used for this call
  - `agentVersionNumber` number: Version number dialed (0 = staging); omitted for prod
  - `agentVersionKind` 'prod' | 'staging' | 'version': prod, or a named version
  - `agentVersionDescription` string: Version name shown on the call when not Prod
  - `phoneNumber` string, required: Phone number
  - `status` 'failed' | 'cancelled' | 'answered_elsewhere' | 'transferring' | 'transferred' | 'failed_during_call' | 'to_be_processed' | 'trying' | 'ringing' | 'busy' | 'missed' | 'in_call' | 'answered' | 'voice_mail' | 'session_progress' | 'scheduled', required: Call status
  - `messages` object[], required: Call messages. Empty when recordingEnabled is false — the verbatim transcript is withheld by policy for calls made with recording disabled.
    - `role` 'Agent' | 'User' | 'Contact' | 'Tool' | 'System', required: Role of the message sender
    - `toolCallId` string, nullable: Tool call identifier if applicable
    - `content` string, required: Message content
    - `timestamp` string, date-time, required: Timestamp when message was sent
    - `seconds` number, required: Duration in seconds
    - `agentId` string, nullable: Agent identifier if applicable
    - `contactId` string, nullable: Contact identifier if applicable
    - `userId` string, nullable: User identifier if applicable
    - `completionTurnMs` number: Completion turn time in milliseconds
    - `voiceTurnMs` number: Voice turn time in milliseconds
    - `totalTurnMs` number: Total turn time in milliseconds
    - `speakerId` number: Speaker identifier
    - `participantLabel` string: Label of the participant (e.g. "Caller: 6982364479", "Transfer: 2103009090")
    - `translatedContent` string: Live-translated version of `content`, when live translation was active for this turn.
    - `originalLanguage` string: Detected spoken language of `content` (ISO 639-1, e.g. "de").
    - `translatedLanguage` string: Target language of `translatedContent` (ISO 639-1, e.g. "el").
  - `recordingEnabled` boolean: Whether recording was enabled by policy when the call ran. false → messages/aiDialogue/transcription files are withheld (summary and post-analysis remain); absent on older calls (treated as recorded).
  - `recordingFileId` string: Recording file identifier
  - `humanRecordingFileId` string: Human recording file identifier
  - `originalRecordingFileId` string: Original (untranslated) recording file identifier, present when live translation was active
  - `transcriptionFileId` string: Transcription file identifier
  - `humanTranscriptionFileId` string: Human transcription file identifier
  - `endCallReason` 'caller_hungup' | 'user_hungup' | 'caller_voice_mail' | 'caller_does_not_exist' | 'wrong_set_number' | 'call_maximum_time_reached' | 'caller_unresponsive_after_nudges' | 'call_transferred' | 'caller_busy' | 'caller_declined' | 'no_channels' | 'unavailable' | 'voip_phone_offline' | 'voip_phone_service_failed' | 'no_agent_id' | 'phone_number_not_found' | 'agent_not_found' | 'no_remote_rtp_address' | 'no_selected_codec' | 'no_remote_rtp_port' | 'voip_provider_issue' | 'inbound_calls_blocked' | 'no_rtp_stream' | 'no_pickup_after_ringing_max_seconds' | 'sdp_error' | 'not_acceptable_here' | 'voip_temporary_issue' | 'voip_phone_not_exist' | 'caller_does_not_speak' | 'message_left' | 'no_channels_available_in_plan' | 'forbidden' | 'failed_to_connect' | 'call_stuck' | 'no_seconds_available_in_plan' | 'maximum_time' | 'subscription_issue' | 'incorect_code' | 'transfer_request_true' | 'transfer_request_false' | 'no_voip_connection' | 'insufficient_balance_in_voip_provider' | 'caller_called_back' | 'campaign_not_active' | 'outside_campaign_schedule' | 'agent_handoff' | 'off_hours_welcome_message' | 'answered_elsewhere': End call reason
  - `sipFailure` object: What the carrier said when it refused the call (failed calls only)
    - `code` number, required: SIP status code of the final INVITE response
    - `reason` string: SIP reason phrase as the carrier sent it
    - `warning` string: SIP Warning header
    - `q850` number: Q.850 cause from the Reason header
    - `kind` 'number_not_in_service' | 'invalid_number' | 'rejected' | 'barred' | 'phone_unreachable' | 'network_busy' | 'no_credit': Plain-language category for the user
  - `duration` number: Call duration in seconds
  - `hertz` number, required: Audio frequency in hertz
  - `humanInCall` boolean: Whether human is in call
  - `summary` string: Call summary
  - `shortSummary` string: One-line summary for call cards
  - `callDirection` 'inbound' | 'outbound', required: Call direction
  - `voiceCost` object: Schema for call voice cost information
    - `cost` number, required: Current cost
    - `savedCost` number, required: Saved cost
    - `totalCost` number, required: Total cost
    - `characters` number, required: Number of characters
    - `charactersSaved` number, required: Number of characters saved
    - `totalCharacters` number, required: Total number of characters
  - `transcriptionCost` number: Transcription cost
  - `completionCost` object: Schema for call completion cost information
    - `inputTokens` number, required: Number of input tokens
    - `cachedInputTokens` number: Number of cached input tokens (served from prompt cache)
    - `cacheWriteTokens` number: Input tokens written to the provider prompt cache (billed at a premium on GPT-5.6+)
    - `outputTokens` number, required: Number of output tokens
    - `reasoningTokens` number: Number of reasoning tokens (for reasoning models like o1, o3, gpt-5)
    - `totalTokens` number, required: Total number of tokens
    - `inputCost` number, required: Input cost
    - `cachedInputCost` number: Cached input cost (typically 90% cheaper than regular input)
    - `completionCost` number, required: Completion cost
    - `reasoningCost` number: Reasoning tokens cost (billed as output tokens)
    - `totalCost` number, required: Total cost
    - `callCompletionCost` number: Call completion cost
    - `analysisCompletionCost` number: Analysis completion cost
    - `speculative` boolean: This request was a speculative pre-<end> fire (tag rides through to ModelUsage)
  - `totalCost` number: Total cost
  - `consumerCost` number: Total Cost of the Call for the Consumer
  - `dynamicVariables` object: Dynamic variables
  - `source` 'app' | 'api' | 'web' | 'mcp' | 'phone' | 'mobile' | 'mobile-bridge' | 'scheduled' | 'waitlist' | 'campaign' | 'workflow' | 'recording' | 'transfer' | 'google-meet' | 'meeting' | 'whatsapp' | 'viber' | 'telegram' | 'instagram' | 'instagram_comment' | 'messenger' | 'messenger_comment' | 'email' | 'vicidial-seat' | 'campaign-seat' | 'vicidial' | 'phone-verification': Source of the call
  - `mobileTransferKind` 'pin_bridge' | 'busy_forward' | 'callback' | 'no_answer_forward': Mobile transfer mode: pin_bridge or busy_forward
  - `mobileUserId` string: Mobile seat user id for PIN bridge / busy-forward
  - `mobileTransferAiFallback` boolean: Busy-forward fell back to AI for the waiting caller
  - `mobileTransferAiFallbackReason` string: Why busy-forward fell back to AI
  - `messageOnly` boolean: Personal assistant with no voice minutes: welcome message, then recorded the caller only (analysis minutes)
  - `mobileBridgeNoDestLeg` boolean: Hidden bridge leg whose transfer never produced a destination leg — kept visible so the call is not lost from the grid
  - `participants` object[]: Conversational participants β€” Agent / User / Contact. The carrier/trunk is transport, not a participant. Append-only with lifecycle status.
    - `type` 'ai' | 'trunk' | 'softphone' | 'app' | 'transfer' | 'meet': Legacy transport-flavoured type. New writers populate `role` + `deviceKind` instead.
    - `role` 'Agent' | 'User' | 'Contact': Conversational role β€” Agent (AI), User (VL user being reached on a device), or Contact (external CRM contact). Mirrors `CallMessage.role`. The carrier/trunk is transport, not a participant.
    - `status` 'invited' | 'ringing' | 'joined' | 'left' | 'no_answer' | 'rejected' | 'failed' | 'cancelled' | 'unreachable': Lifecycle status. Transitions invited β†’ joined β†’ left on the happy path; invited β†’ no_answer/rejected/failed/cancelled/unreachable otherwise.
    - `invitedAt` string, date-time: ISO 8601 date string
    - `joinedAt` string, date-time: ISO 8601 date string
    - `leftAt` string, date-time: ISO 8601 date string
    - `deviceKind` 'app' | 'sip' | 'mobile' | 'trunk' | 'meet': How the participant is connected. `app`/`sip`/`mobile` for VL users; `trunk` for an external party reached via SIP carrier; `meet` for Google Meet participants.
    - `participantId` string: Stable id for this participant within the call (Rust sub-call ID for RTP legs, Meet participant resource name for Meet)
    - `extensionNumber` string: Internal extension number for softphone / app participants β€” the user's extensionNumber at the time the call leg was created. Static for the call, no rename concern.
    - `phoneNumber` string: Dialed / caller external phone number for trunk / transfer / meet participants. Captured at participant create time; for Contact display, prefer the live `contact.mainPhoneNumber` first and fall back to this.
    - `callId` string: Rust-side call ID for this channel
    - `codec` string: Codec used (PCMA, PCMU, G722, PCM16, …). Sample rate is `hertz`, not part of the codec name.
    - `hertz` number: Audio sample rate in Hz (8000 for SIP and the app UDP leg).
    - `rtpPort` number: Allocated RTP port on Rust
    - `serverIp` string: Rust server IP
    - `remoteAddress` string: Remote RTP address
    - `remotePort` number: Remote RTP port
    - `transcribe` boolean: Whether to start transcription for this participant
    - `initiator` boolean: Whether this participant initiated the call (hangup = end for all)
    - `contactId` string: Contact ID for this participant (external party β€” CRM contact)
    - `contactName` string: Denormalized contact display name for fast UI rendering β€” set at participant create time so the frontend doesn't have to refetch the Contact doc just to label a transcript turn.
    - `contact` object: Any valid JSON value
    - `userId` string: VL user ID for this participant (internal user on their mobile/app β€” distinct from contactId which is a CRM contact)
    - `userName` string: Denormalized VL user name for fast UI rendering. Same rationale as contactName.
    - `user` object: Any valid JSON value
    - `agentId` string: AI Agent ID when this participant is the AI leg (type=ai) or when the participant is being analyzed/transcribed by a specific agent (type=trunk/softphone/app on a transcriptionOnly call). Lets the frontend show "Sales Agent" next to the AI's turns and link to the agent definition.
    - `agentName` string: Denormalized agent name for fast UI rendering.
    - `agent` object: Any valid JSON value
    - `displayName` string: Display name (Meet/Zoom/Teams)
    - `email` string: Email address (Meet/Zoom/Teams)
    - `isOrganizer` boolean: Whether this participant is the meeting organizer (Meet/Zoom/Teams)
    - `joinKind` 'signed_in' | 'phone' | 'anonymous': How the participant joined (Meet/Zoom/Teams)
    - `durationSeconds` number: Total seconds the participant was joined (Meet/Zoom/Teams)
  - `ringTargets` object[]: Per-device dial attempts when one participant was forked to multiple devices.
    - `participantId` string, required: Participant this ring target belongs to. Same value as the participant's `participantId`.
    - `userId` string: VL user being reached (when the participant is role=User).
    - `contactId` string: CRM contact being reached (when the participant is role=Contact).
    - `deviceKind` 'app' | 'sip' | 'mobile' | 'trunk' | 'meet', required: Which device kind was tried.
    - `deviceId` string: Identifier for the specific device: UserEndpoint id, ExtensionRegistration id, or phone-number hash for mobile-callback.
    - `address` string: Dialed address β€” SIP URI, mobile number, etc. For debugging / CDR display.
    - `status` 'invited' | 'ringing' | 'joined' | 'left' | 'no_answer' | 'rejected' | 'failed' | 'cancelled' | 'unreachable', required: Outcome of this ring attempt.
    - `startedAt` string, date-time, required: When the INVITE / push went out.
    - `endedAt` string, date-time: ISO 8601 date string
    - `failureReason` string: Free-text reason when status is failed/unreachable (e.g. "no registration", "5xx from softphone", "carrier rejected").
  - `meetRecordingDriveFileId` string: Google Drive file ID of the Meet recording (only for source=google-meet)
  - `meetAlternateDriveFileIds` string[]: Other teammates Drive copies of the same Meet recording
  - `meetConferenceRecordId` string: Google Meet conferenceRecord resource name (only for source=google-meet)
  - `meetParticipantEmails` string[]: Resolved participant email addresses (only for source=google-meet)
  - `meetOrganizerEmail` string: Email of the meeting organizer (only for source=google-meet)
  - `analysedByAi` 'pending' | 'failed' | 'created' | 'completed': Whether the call was analysed by AI
  - `metadata` object: Metadata - Common examples include: appointmentReminder (true/false), reminderMinutes (number as string), eventId, eventIds (JSON string), calendarId, contactId, agentId, callId, voipPhoneId, phoneNumber, totalAppointments (number as string), callBack (true/false), viewUrl, viewHumanRecordingUrl, priority (high/medium/low), category (support/sales/billing), source (inbound/outbound), campaign_type, lead_score
  - `superVariables` object: Super variables - Typed key-value pairs with placeholders for advanced call data
  - `contextForAi` string: Context for AI
  - `llmConversationId` string: LLM conversation ID for maintaining stateful context across provider APIs (Gemini Interactions, OpenAI Responses, etc.)
  - `postAnalysisResults` object: Post analysis results - AI-generated analysis data from call processing (includes scalars and arrays such as email_recipients / email_cc_recipients / sms_recipients)
  - `feedback` object: Post-conversation customer satisfaction feedback (widget or SMS form)
    - `rating` number, required
    - `comment` string
    - `createdAt` string, date-time: ISO 8601 date string
    - `channel` 'widget' | 'sms'
  - `callerVerification` object: Caller verification status (OTP or Google; no OTP stored)
    - `status` 'unverified' | 'pending' | 'verified', required
    - `channel` 'email' | 'sms' | 'google' | 'known_field'
    - `associateId` string
    - `verifiedAt` string, date-time: ISO 8601 date string
  - `encryptedId` string: Encrypted identifier - Encrypted call identifier for security
  - `campaignId` string: Campaign identifier
  - `campaignLeadId` string: Campaign lead identifier
  - `outboundResult` object: Outbound call result
    - `humanJoined` boolean
    - `humanResponded` boolean: Callee spoke — not SIP answer, not voicemail
    - `askedToNeverCallAgain` boolean
    - `askedToCallBackLater` boolean
    - `dateTimeToCallBack` string, date-time: ISO 8601 date string
  - `rtpPort` number: RTP port
  - `rtpIp` string: RTP IP address
  - `startAt` string, date-time: ISO 8601 date string
  - `endAt` string, date-time: ISO 8601 date string
  - `contact` object: Any valid JSON value
  - `user` object: Any valid JSON value
  - `voipPhone` object: Any valid JSON value
  - `callInformation` object: Call information
  - `agent` object: Any valid JSON value
  - `recordingFile` object: Any valid JSON value
  - `humanRecordingFile` object: Any valid JSON value
  - `originalRecordingFile` object: Any valid JSON value
  - `transcriptionFile` object: Any valid JSON value
  - `humanTranscriptionFile` object: Any valid JSON value
  - `campaign` object: Any valid JSON value
  - `campaignLead` object: Any valid JSON value
  - `aiDialogue` union[]: AI dialogue messages
    - union
      - object
        - `role` 'developer', required
        - `content` union, required
          - string
          - object[]
            - `text` string, required
            - `type` 'text', required
        - `name` string
      - object
        - `role` 'system', required
        - `content` union, required
          - string
          - object[]
            - `text` string, required
            - `type` 'text', required
        - `name` string
      - object
        - `role` 'user', required
        - `content` union, required
          - string
          - union[]
            - union
              - …
        - `name` string
      - object
        - `role` 'assistant', required
        - `content` union
          - string
          - object[]
            - `text` string, required
            - `type` 'text', required
          - object[]
            - `refusal` string, required
            - `type` 'refusal', required
        - `name` string
        - `tool_calls` object[]
          - `id` string, required
          - `type` 'function', required
          - `function` object, required
            - `name` string, required
            - `arguments` string, required
        - `function_call` object, nullable
          - `name` string, required
          - `arguments` string, required
        - `audio` object, nullable
          - `id` string, required
        - `refusal` string, nullable
      - object
        - `role` 'tool', required
        - `content` union, required
          - string
          - object[]
            - `text` string, required
            - `type` 'text', required
        - `tool_call_id` string, required
      - object
        - `role` 'function', required
        - `content` string, nullable
        - `name` string, required
  - `sensitiveData` object: Placeholders in this call's transcript and their last four characters. Real values: the reveal endpoint (sensitive-data permission, audited).
    - `entries` object[], required
      - `placeholder` string, required
      - `type` 'amka' | 'afm' | 'personal_number' | 'id_card' | 'passport' | 'gemi' | 'cy_tic' | 'iban' | 'card' | 'cvv' | 'pin' | 'email' | 'phone', required
      - `last4` string, required
    - `expiresAt` string, date-time: ISO 8601 date string
    - `recordingRedactedAt` string, date-time: ISO 8601 date string
    - `clipPlaceholders` string[]
  - `scheduledAt` string, date-time: ISO 8601 date string
  - `retries` number: Number of retries
  - `maxRetries` number: Maximum number of retries
  - `retryDelayMinutes` number: Retry delay in minutes
  - `transferringDetails` object: Transferring details
    - `callSourceId` string, required: Call source ID
    - `callSourceCallId` string, required: Call source call ID
    - `transferringStatus` 'transferring' | 'pending_confirmation' | 'success' | 'failed' | 'aborted', required: Transferring status
    - `transferFailedReason` string: The reason the transfer failed
    - `callDestinationId` string: Call destination ID
    - `callDestinationCallId` string: Call destination call ID
    - `announceTransferReason` boolean: Whether to announce the transfer reason
    - `transferReason` string: The transfer reason
    - `showCallerId` boolean: Whether to show the caller ID
    - `callerId` string, required: The caller ID
    - `transferMode` 'blind' | 'warm' | 'attended': Transfer mode for this in-flight transfer
    - `blindTransfer` boolean: Deprecated — use transferMode
    - `consultPhase` 'idle' | 'consulting_ringing' | 'consulting_dest' | 'back_with_caller' | 'merged' | 'cancelled': Warm/attended consult: dest rings, talk to dest, swap back to caller, then merge or cancel
    - `consultOperatorCallId` string: Operator call (B) that placed this consult leg
    - `secondsToWaitForCallerToAnswer` number: The number of seconds to wait for the caller to answer the call
    - `forwardedFrom` string: The number the call was forwarded from
    - `destinationType` union, required: The type of the destination to transfer the call to
      - 'User' | 'External' | 'Agent'
      - 'UserGroup'
      - 'UserEndpoint'
    - `destinationId` string: The id of the destination (userId / userGroupId / agentId). Used to route an internal blind transfer looped back to our DID; destinationNumber still carries the dialable number for External.
    - `destinationNumber` string, required: The number to transfer the call to
    - `agentIdToTransferTo` string: The agent ID to transfer the call to
    - `agentVersionIdToTransferTo` string: Optional saved agent version to load after transfer
    - `messageToPersonWantingToBeTransferred` string: The message to the person wanting to be transferred
    - `analyzeTransferredCall` boolean: Whether to analyze the transferred call
    - `agentIdToAnalyzeTransferredCall` string: The agent ID to analyze the transferred call
    - `routedVia` 'outbound_route' | 'customer_pbx_inbound_fallback' | 'remote_pbx_extension_fallback' | 'provider_trunk_inbound_fallback': How the outbound leg was routed for an external transfer.
    - `outboundTrunkId` string: SipTrunk id used for the outbound leg of an external transfer.
    - `outboundRouteId` string: OutboundRoute id that matched the destination, when an explicit route was used.
    - `inviteSentAt` string, date-time: ISO 8601 date string
    - `referAcceptedAt` string, date-time: ISO 8601 date string
  - `transferAttempts` object[]: History of finished transfer attempts (multi-attempt calls)
    - `destinationNumber` string: The number the transfer was attempted to
    - `destinationType` 'User' | 'External' | 'Agent': The type of the transfer destination
    - `status` 'transferring' | 'pending_confirmation' | 'success' | 'failed' | 'aborted', required: Terminal outcome of this attempt (success/failed)
    - `transferReason` string: Why the agent transferred the call
    - `transferFailedReason` string: Why this attempt failed, when recorded
    - `at` string, date-time, required: When the attempt reached its terminal status
  - `chatHandoff` object: Live WhatsApp/widget human takeover — current offer or owner
    - `status` 'none' | 'offering' | 'live' | 'offering_retransfer', required
    - `ownerUserId` string: Write lock — only this user may send live messages
    - `ownerUserName` string
    - `offeredUserId` string
    - `offeredUserIds` string[]: Every user currently ringing on a group offer. First accept cancels the rest.
    - `offeredUserName` string
    - `offeredByUserId` string: AI offer leaves this empty; retransfer sets the previous owner
    - `destinationType` 'UserEndpoint' | 'UserGroup'
    - `destinationId` string: UserEndpoint or UserGroup id currently offered or owned
    - `reason` string
    - `offeredAt` string, date-time: ISO 8601 date string
    - `acceptedAt` string, date-time: ISO 8601 date string
    - `secondsToWait` number
  - `chatHandoffAttempts` object[]: History of finished chat handoff offers
    - `fromUserId` string: Previous owner, if this was a user-to-user retransfer. Omitted when the AI offered.
    - `toUserId` string, required: User who was offered the thread
    - `toUserName` string
    - `destinationId` string: UserEndpoint id that was offered
    - `reason` string
    - `outcome` 'accepted' | 'declined' | 'timeout' | 'cancelled', required
    - `at` string, date-time, required: ISO 8601 date string
  - `networkQuality` object: Network quality summary (packet loss/jitter) from the RTP server
    - `packetsReceived` number, required: Total RTP packets received from the caller
    - `packetsLost` number, required: Packets lost (from RTP sequence gaps)
    - `lossPct` number, required: Cumulative packet loss percentage (0-100)
    - `packetsOutOfOrder` number, required: Packets that arrived duplicated or out of order
    - `jitterMs` number, required: Smoothed inter-arrival jitter in milliseconds
    - `farEnd` object: The far end view of the audio we sent it, from its RTCP reception reports
      - `reports` number, required: How many RTCP reception reports the far end sent
      - `lossPct` number, required: Loss of OUR audio in the most recent report (0-100)
      - `worstLossPct` number, required: Worst loss of OUR audio seen in any report (0-100)
      - `packetsLost` number, required: Cumulative packets of ours the far end never received
      - `jitter` number, required: Jitter the far end measured on our stream (RTP timestamp units)
  - `scenarioTest` object: Set when this call is a live agent-scenario test leg
    - `scenarioId` string, required: AgentScenario under test
    - `resultId` string, required: AgentScenarioResult row this leg belongs to
    - `runId` string: Batch run id shared by all repetitions
    - `role` 'caller' | 'agent_under_test', required: caller = simulator leg, agent_under_test = inbound leg graded by the judge
    - `useRealTools` boolean: When false, tools not mocked by the scenario must be blocked
    - `mockedTools` object[]: Per-tool interception config stamped from the scenario for FunctionCallRunner
      - `toolName` string, required: Function call / tool name to intercept
      - `mode` 'mock' | 'real' | 'block', required: mock = return canned responses, real = execute normally, block = return a generic "unavailable" result
      - `responses` object[]: Sequential responses; the last entry repeats for further invocations
        - `response` string, required: Payload the LLM receives as the tool result
        - `delayMs` number: Artificial latency before returning the mock
        - `fail` boolean: Simulate a tool error instead of a successful result
    - `frozenNow` string, date-time: ISO 8601 date string
    - `callDirection` 'inbound' | 'outbound': Direction the scenario tests. The test leg is always inbound SIP-wise, so "outbound" only makes the agent prompt read as an outgoing call
    - `scenarioName` string: Scenario text, written back after judging so call lists can label the test without a scenario lookup
    - `passed` boolean: Judge verdict written back after evaluation — absent while the run is in progress or errored
    - `score` number: Judge score 1-10 written back after evaluation
    - `abruptEndAfterSeconds` number: Dropped-line simulation: hang this call up this many seconds after it connects, with no goodbye. Armed in-process by RtpService, which owns the call.
  - `originalCallId` string: Original call identifier
  - `scheduledReason` 'retry' | 'customer_callback' | 'dropped_call': Why this call was scheduled: retry, customer_callback (the person asked to be called back) or dropped_call
  - `previousCallId` string: For agent-handoff legs: the `_id` of the previous Call doc that was finalized when the agent identity was swapped. Walking the chain backwards reconstructs the full per-agent timeline.
  - `useAntiHarassmentProtection` boolean: Whether to use anti-harassment protections (daily limits, cooldown, etc.)
  - `overrideTimeRestriction` boolean: Whether to override time restrictions
  - `overrideDayRestriction` boolean: Whether to override day restrictions
  - `isWelcomeMessageAIGenerated` boolean: Whether the welcome message is AI generated
  - `welcomeMessageOverride` string: Per-call welcome message spoken verbatim by the answering agent, overriding its configured first message.
  - `hostedOutbound` boolean: Whether this outbound call used the company/distributor trunk with a verified caller ID.
  - `fallbackForUserId` string: When the AI answered as a direct-to-extension fallback, the user the call was originally routed to. Scopes AI-created callbacks to that user.
  - `aiDisclosure` object: Art. 50 AI disclosure snapshot for this call
    - `required` boolean, required
    - `played` boolean, required
    - `skippedReason` 'disabled' | 'transfer_whisper' | 'session_resume' | 'welcome_skipped' | 'exception'
    - `source` 'welcome_text' | 'composed' | 'none'
    - `matchedTerm` string
    - `language` string
    - `text` string
    - `playedAt` string, date-time: ISO 8601 date string
    - `recordingOffsetMs` number
  - `useOwnApiKey` boolean: Whether the clients is using their own API key
  - `rescheduled` boolean: Whether the call is scheduled
  - `rescheduledAt` string, date-time: ISO 8601 date string
  - `funnelStatus` 'resolved' | 'escalated' | 'dropped' | 'early_abandon' | 'human_handled' | 'technical_issue' | 'frustrated_hung_up': High-level funnel status for analytics (resolved, escalated, dropped, pending)
  - `resolutionStatus` 'converted' | 'appointment_scheduled' | 'lead_qualified' | 'information_provided' | 'issue_resolved' | 'partially_resolved' | 'callback_requested' | 'contact_unavailable' | 'not_interested' | 'do_not_call' | 'wrong_contact': Detailed resolution outcome of the call
  - `escalationReason` 'complex_query' | 'user_requested' | 'user_frustration' | 'knowledge_gap' | 'sensitive_data_required' | 'verification_failed' | 'language_barrier' | 'audio_quality_issues' | 'vip_customer': Reason why call was escalated to human (only applicable if escalated)
  - `conversionStatus` 'converted' | 'not_converted': Whether business goal was achieved (converted, not_converted)
  - `callCategoryIds` string[]: Array of call category identifiers for this call
  - `callCategories` object[]: Call categories information (populated from CallCategory)
  - `postCallAnalysis` 'not_started' | 'in_progress' | 'failed' | 'completed': Status of post call analysis (not-started, in_progress, failed, completed)
  - `postCallAnalysisStages` object[]: Schema for post call analysis stages
    - `action` 'summary' | 'transcription' | 'analysis' | 'workflow' | 'appointment_reminder' | 'send_skroutz_data' | 'update_confirmed_appointment_with_data' | 'has_asked_to_never_call_again' | 'has_asked_for_callback' | 'has_called_dropped' | 'should_schedule_call_back_campaign_lead' | 'auto_create_task' | 'resolve_contact_callbacks' | 'evaluate_conversation' | 'evaluation' | 'knowledge_unanswered' | 'knowledge_gap_answers' | 'knowledge_staff_answers' | 'all' | 'job', required: Action to perform on the stage
    - `status` 'not_started' | 'in_progress' | 'failed' | 'completed', required: Status of the stage
    - `retryCount` number: Number of retry attempts made
    - `lastAttemptAt` string, date-time: ISO 8601 date string
    - `error` string: Error message from the last failed attempt
  - `evaluatedAt` string, date-time: ISO 8601 date string
  - `planId` string: The subscription plan ID active at the time of the call
  - `resellerId` string: The reseller company ID linked with the customer at the time of the call
  - `callHandler` 'voip_phone' | 'pbx_softphone' | 'pbx_trunk' | 'extension': Which system processed the call (voip_phone, pbx_softphone, pbx_trunk)
  - `appClientParticipantId` string: Rust participant sub-call ID for the app client (used to remove it on transfer)
  - `seen` object[]: Users who have marked/viewed this call as seen
    - `seenByUserId` string, required: User who has seen this call
    - `seenAt` string, date-time, required: When the user saw the call
  - `callerCard` object: CRM caller card (matched records + "Open in" links) as shown when the call came in
    - `links` object[], required
      - `provider` string, required
      - `customerKey` string
      - `label` string, required
      - `icon` string, required
      - `url` string, required
      - `kind` 'record' | 'no_match', required
      - `primary` boolean, required
      - `autoOpen` 'off' | 'on_answer' | 'on_ring', required
    - `matches` object[], required
      - `provider` string, required
      - `customerKey` string, required
      - `label` string, required
      - `recordId` string
      - `displayName` string
      - `phone` string
      - `email` string
      - `company` string
      - `url` string
    - `contact` object
      - `id` string, required
      - `name` string
      - `company` string
      - `phone` string
      - `email` string
    - `primaryProvider` string
    - `autoOpenUrl` string
    - `autoOpen` 'off' | 'on_answer' | 'on_ring', required
    - `builtAt` string, date-time, required: ISO 8601 date string
  - `isPrivate` boolean: Private call — visible only to its own user and super-admins
  - `legalHold` boolean: Legal hold — retention and deletion skip this call until released
  - `analysisOnDemand` boolean: Analysis on demand: the call was recorded but not transcribed or analysed; POST /phones/call/:id/analyze runs it
  - `analysisRequestedAt` string, date-time: ISO 8601 date string

## Other responses

- `401`: Unauthorized
- `403`: Invalid CSRF
- `404`: Call not found
- `429`: Rate limit exceeded
- `500`: Internal Server Error

---

[API](https://skmtc.dev/voicelogica/apis/voice-logica-api.md) · [All operations](https://skmtc.dev/voicelogica/apis/voice-logica-api/llms.txt) · [OpenAPI document](https://skmtc.dev/voicelogica/apis/voice-logica-api/revisions/30f76840084e?raw)
