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

# ListVoiceCallsAPI mirrors erdo_voice_call_list. The filters, the since/until


> window (RFC3339 on \`created\_at\`; since inclusive, until exclusive) and paging ride the query string; the organization always comes from the caller's identity. \`total\` counts every call matching the filters and window, independent of paging.

Each row carries the lead the call produced: \`contact\` (null = not analysed yet, {} = analysed and nobody reachable, an object = the caller's details, phone seeded from their caller ID), \`lead\_status\` ("" while awaiting, then created | existing | disabled | test), \`lead\_saved\_at\`, and the permanent lead it resolved to as \`canonical\_lead\_id\` / \`lead\_reference\` (both empty when none). The \`canonical\_lead\_id\` query parameter (a lead UUID or its 22-character reference) narrows to one lead's calls.




## OpenAPI

````yaml /api/openapi.json get /v1/voice/calls
openapi: 3.0.0
info:
  title: Erdo API
  description: >-
    Erdo's REST API: query and write datasets, run agents, manage threads,
    integrations, pages, evals, workstreams, experiments, and bounded outreach.
    Authenticate with a Bearer API key (erdo_api_...) or scoped token
    (erdo_token_...).
  version: '2026-09-14'
servers:
  - url: https://api.erdo.ai
    description: Production
security:
  - bearerAuth: []
paths:
  /v1/voice/calls:
    get:
      summary: >
        ListVoiceCallsAPI mirrors erdo_voice_call_list. The filters, the
        since/until
      description: >
        window (RFC3339 on \`created\_at\`; since inclusive, until exclusive)
        and paging ride the query string; the organization always comes from the
        caller's identity. \`total\` counts every call matching the filters and
        window, independent of paging.


        Each row carries the lead the call produced: \`contact\` (null = not
        analysed yet, {} = analysed and nobody reachable, an object = the
        caller's details, phone seeded from their caller ID), \`lead\_status\`
        ("" while awaiting, then created | existing | disabled | test),
        \`lead\_saved\_at\`, and the permanent lead it resolved to as
        \`canonical\_lead\_id\` / \`lead\_reference\` (both empty when none).
        The \`canonical\_lead\_id\` query parameter (a lead UUID or its
        22-character reference) narrows to one lead's calls.
      operationId: GET:mcp.ListVoiceCallsAPI
      parameters:
        - allowEmptyValue: true
          explode: true
          in: query
          name: agent
          required: false
          schema:
            type: string
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: direction
          required: false
          schema:
            type: string
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: since
          required: false
          schema:
            type: string
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: until
          required: false
          schema:
            type: string
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: limit
          required: false
          schema:
            format: int64
            type: integer
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: offset
          required: false
          schema:
            format: int64
            type: integer
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: cursor
          required: false
          schema:
            type: string
          style: form
        - allowEmptyValue: true
          explode: true
          in: query
          name: canonical_lead_id
          required: false
          schema:
            type: string
          style: form
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  conversations:
                    items:
                      $ref: '#/components/schemas/voice.CallConversationSummary'
                    type: array
                  count:
                    format: int64
                    type: integer
                  limit:
                    format: int64
                    type: integer
                  next_cursor:
                    type: string
                  offset:
                    format: int64
                    type: integer
                  total:
                    description: >
                      canonical\_lead\_id, since/until) at the moment of THIS
                      request, read in the same snapshot as this page and
                      independent of limit/offset/cursor — the same field the
                      widget conversation list carries. It is live: a call
                      captured between two page requests changes it, so a walk
                      across pages is complete when next\_cursor is absent,
                      never when the rows collected reach Total. It is what a
                      reporting tile counts without fetching every row.
                    format: int64
                    title: >
                      Total is how many calls match the filter (agent,
                      direction,
                    type: integer
                type: object
          description: Success response
        default:
          $ref: '#/components/responses/APIError'
components:
  schemas:
    voice.CallConversationSummary:
      description: >
        whether a transcript was captured, never the transcript payload itself
        (the list query does not select transcript\_json — TOAST discipline).
      properties:
        call_id:
          type: string
        call_successful:
          type: string
        canonical_lead_id:
          description: >
            the id to match a lead page on, rather than the caller's number.
            Empty while the call awaits its write, when the lead dataset is not
            enrolled in lead identity, when capture was off, and on every call
            recorded before conversations carried the identity (there is no
            backfill).
          title: >
            CanonicalLeadID is the permanent lead this call's lead write
            resolved to —
          type: string
        consent_basis:
          type: string
        contact:
          allOf:
            - $ref: '#/components/schemas/leadcontact.Contact'
          description: >
            transcript and the carrier-verified caller ID. Deliberately NOT
            omitempty: null means "not analysed yet" and {} means "analysed,
            nobody reachable" — different facts, and only inbound calls are ever
            analysed at all.
          title: >
            Contact is what the caller gave, extracted by the summary cron from
            the
          nullable: true
        created_at:
          format: date-time
          type: string
        direction:
          description: |
            (Erdo placed the call).
          title: >
            Direction is "inbound" (someone called the agent's number) or
            "outbound"
          type: string
        duration_seconds:
          format: int32
          maximum: 2147483647
          minimum: -2147483648
          type: integer
        ended_at:
          format: date-time
          type: string
        from_number:
          description: >
            inbound call, and the agent's own caller ID on an outbound one.
            Empty on rows written before the column existed.
          title: |
            FromNumber is the line the call came FROM: the caller's number on an
          type: string
        has_transcript:
          type: boolean
        lead_reference:
          description: |
            link or an SMS carries; empty exactly when CanonicalLeadID is.
          title: >
            LeadReference is the same identity as a 22-character reference, the
            form a
          type: string
        lead_saved_at:
          format: date-time
          title: |
            LeadSavedAt is when that outcome was recorded, null while awaiting.
          type: string
          nullable: true
        lead_status:
          description: |
            awaiting one, then created | existing | disabled | test.
          title: >
            LeadStatus is what the lead writer did with this call: "" while it
            is still
          type: string
        provider_agent_id:
          description: >
            Recorded on inbound rows (it is their ownership handle); empty on
            older outbound rows, which predate the column.
          title: >
            ProviderAgentID identifies the voice agent that held the
            conversation.
          type: string
        recipient_name:
          type: string
        status:
          type: string
        to_number:
          title: >
            ToNumber is the number dialled — the agent's own number on an
            inbound call.
          type: string
        transcript_summary:
          type: string
      title: |
        CallConversationSummary is one row of an org's call list: metadata and
      type: object
    leadcontact.Contact:
      description: >
        ones the leads dataset has columns for; there is deliberately no
        company, because nothing downstream would use it.
      properties:
        email:
          type: string
        first_name:
          type: string
        last_name:
          type: string
        phone:
          type: string
      title: >
        Contact is what a person told us about themselves. The four fields are
        the
      type: object
  responses:
    APIError:
      content:
        application/json:
          schema:
            externalDocs:
              url: https://pkg.go.dev/encore.dev/beta/errs#Error
            properties:
              code:
                description: Error code
                example: not_found
                externalDocs:
                  url: https://pkg.go.dev/encore.dev/beta/errs#ErrCode
                type: string
              details:
                description: Error details
                type: object
              message:
                description: Error message
                type: string
            title: APIError
            type: object
      description: Error response
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An Erdo API key (erdo_api_...) or scoped token (erdo_token_...).

````