# Bulk call results (/docs/api-reference/bulk-calls/listBulkCallLines)

> Per-contact results for a campaign: what happened on each call, the variables you sent with that contact, and a pointer to the recording.

**GET** `/calls/bulk_call/{bulk_call_id}/lines`

Per-contact results for a campaign: what happened on each call, the
variables you sent with that contact, and a pointer to the recording.

## Paging

There is one rule. Call it with no `cursor`, then keep passing back the
`next_cursor` you were handed until it comes back `null`.

```
cursor = None
while True:
    page = GET /lines?pagesize=150&cursor={cursor}
    handle(page["records"])
    cursor = page["next_cursor"]
    if not cursor: break
```

Each call returns a page of rows, oldest first: `pagesize` goes up to
150 and defaults to 30. Cursors are opaque, so pass back the string you
were given and never build one. No contact is skipped or returned
twice, even while the campaign is still dialing.

## Transcripts are not in the row

Each row carries `call.recording_id`, not the conversation. Transcripts
reach 212 KB, so carrying them here would make one page tens of
megabytes. Fetch the one you want from
`GET /calls/logs/{recording_id}`.

```yaml
operationId: listBulkCallLines
parameters:
  - in: path
    name: bulk_call_id
    required: true
    schema:
      type: integer
    description: Id of the bulk call campaign.
  - in: query
    name: cursor
    schema:
      type: string
    description: |
      The `next_cursor` from your previous response. Omit it on the first
      request. Opaque: pass it back unchanged.
  - in: query
    name: pagesize
    schema:
      type: integer
      default: 30
      maximum: 150
    description: Rows per page. Above 150 the request is refused.
  - in: query
    name: call_status
    schema:
      type: string
      enum:
        - Pending
        - In Progress
        - completed
        - voicemail_detected
        - no-answer
        - busy
        - Failed
        - Skipped
        - retry_scheduled
        - cancelled
    description: Return only contacts in this state.
  - in: query
    name: interaction_status
    schema:
      type: string
    description: Return only contacts with this interaction outcome.
  - in: query
    name: search
    schema:
      type: string
    description: |
      An exact phone number, matched against the contact's number and the
      number that called it. Not a substring search.
  - in: query
    name: include_total
    schema:
      type: boolean
      default: false
    description: |
      Add `total_records` to the response. It costs a count over the whole
      filtered campaign, so it is off unless you ask. Ask for it once to
      fill a header, not on every page of a walk.
responses:
  '200':
    description: |
      One page of results. `next_cursor` is `null` on the last page.
    content:
      application/json:
        schema:
          type: object
          properties:
            status:
              type: string
            bulk_call_id:
              type: integer
            records:
              type: array
              items:
                type: object
                description: One contact's result inside a bulk-call campaign.
                properties:
                  id:
                    type: integer
                    description: Id of this contact row in the campaign.
                  to_number:
                    type: string
                  from_number:
                    type: string
                    description: |
                      The number this contact was called from. With a rotation pool this
                      varies between contacts.
                  call_status:
                    type: string
                  interaction_status:
                    type: string
                  failed_reason:
                    type: string
                    nullable: true
                  dispatched_at:
                    type: string
                    nullable: true
                    description: '`YYYY-MM-DD HH:MM:SS`, UTC.'
                  custom_variables:
                    type: object
                    description: The variables submitted with this contact.
                    additionalProperties: true
                  metadata:
                    type: object
                    additionalProperties: true
                  retry_attempt:
                    type: integer
                  retry_scheduled:
                    type: boolean
                  retry_scheduled_datetime:
                    type: string
                    nullable: true
                  reschedule_requested:
                    type: boolean
                  reschedule_datetime:
                    type: string
                    nullable: true
                  reschedule_status:
                    type: string
                  call:
                    type: object
                    nullable: true
                    description: |
                      Null until the contact has actually been called, so a queued or
                      skipped row has no `call`.
                    properties:
                      recording_id:
                        type: integer
                        description: |
                          Fetch the transcript with `GET /calls/logs/{recording_id}`. The
                          transcript is not included here on purpose: they reach 212 KB,
                          which would make a full page tens of megabytes.
                      time_of_call:
                        type: string
                      duration_seconds:
                        type: number
                      duration_minutes:
                        type: number
                      recording_url:
                        type: string
                      call_status:
                        type: string
                      sentiment_score:
                        type: string
                      extracted_variables:
                        type: object
                        description: What the agent extracted during this call.
                        additionalProperties: true
                      is_voicemail:
                        type: boolean
                      answering_machine_detected:
                        type: boolean
            pagesize:
              type: integer
            has_more:
              type: boolean
            next_cursor:
              type: string
              nullable: true
              description: Pass back as `cursor`. `null` means you are done.
            total_records:
              type: integer
              description: Only present when `include_total` is true.
        example:
          status: success
          bulk_call_id: 314
          records:
            - id: 156
              to_number: '+15551234567'
              from_number: '+15559876543'
              call_status: completed
              interaction_status: ''
              failed_reason: null
              dispatched_at: '2026-08-26 14:46:02'
              custom_variables:
                contact_name: Ravi Kumar
                company_name: Acme Corp
              metadata: {}
              retry_attempt: 1
              retry_scheduled: false
              retry_scheduled_datetime: null
              reschedule_requested: false
              reschedule_datetime: null
              reschedule_status: pending
              call:
                recording_id: 50585
                time_of_call: '2026-08-26 14:46:21'
                duration_seconds: 9
                duration_minutes: 0
                recording_url: https://backend.omnidim.io/api/v1/recording/50585?token=9d35be29
                call_status: completed
                sentiment_score: ''
                extracted_variables: {}
                is_voicemail: false
                answering_machine_detected: false
          pagesize: 150
          has_more: true
          next_cursor: MTU2
```

**cURL**

```bash
curl "https://backend.omnidim.io/api/v1/calls/bulk_call/314/lines?pagesize=150" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**curl**

```bash
curl -X GET "https://omnidim.io/api/v1/calls/bulk_call/{bulk_call_id}/lines" \
  -H "Authorization: Bearer $OMNIDIM_API_KEY"
```