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

# List enrolled contacts

> Enrolled contacts and their per-contact state, oldest enrollment first. Required
scope `campaigns:read`.


Per-contact state for one campaign, oldest enrollment first. Each row carries the enrollment's own state machine (status, attempts used, next retry time, why it stopped) with the underlying `lead` and the `last_disposition` expanded to the full objects they reference.

Use this to answer "what happened to this specific person". For aggregate counters use `GET /campaigns/{id}/stats`.

Required scope: `campaigns:read`.

## Path parameters

| Parameter | Type | Notes                                                |
| --------- | ---- | ---------------------------------------------------- |
| `id`      | uuid | Campaign ID. Must belong to the API key's workspace. |

## Query parameters

| Parameter | Type    | Default | Notes                                                                                      |
| --------- | ------- | ------- | ------------------------------------------------------------------------------------------ |
| `status`  | string  | —       | Comma-separated list of contact statuses to keep. Whitespace around each value is trimmed. |
| `limit`   | integer | `50`    | Page size. Clamped to `1`–`200`.                                                           |
| `offset`  | integer | `0`     | Rows to skip. Negative values are treated as `0`.                                          |

## Contact statuses

| Status                 | Meaning                                                                               |
| ---------------------- | ------------------------------------------------------------------------------------- |
| `pending`              | Waiting for its turn.                                                                 |
| `scheduled`            | Waiting for `next_attempt_at` to elapse.                                              |
| `dialing`              | A call is being placed or is in progress.                                             |
| `awaiting_disposition` | The call ended and is waiting to be categorized. Never re-dialed while in this state. |
| `completed_success`    | A stop rule fired — this contact is done.                                             |
| `completed_failed`     | Finished without a successful outcome.                                                |
| `exhausted`            | Reached `max_attempts` without hitting a stop rule.                                   |
| `excluded`             | Removed by you, or retired when the campaign was stopped or archived. Terminal.       |
| `dnc`                  | On the do-not-call list. Never called again.                                          |

## Row fields

| Field                       | Type              | Meaning                                                                                                                                                         |
| --------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                        | uuid              | Enrollment ID (not the lead ID).                                                                                                                                |
| `lead_id`                   | uuid              | The lead this enrollment points at.                                                                                                                             |
| `to_number_e164`            | string            | The number snapshotted at enrollment. Editing the lead afterwards cannot silently redirect a dial.                                                              |
| `status`                    | string            | See above.                                                                                                                                                      |
| `stop_hit`                  | boolean           | `true` once a stop rule retired this contact. Never un-set.                                                                                                     |
| `stop_reason`               | string \| null    | Why it stopped — e.g. `disposition`, `stop_disposition`, `no_answer`, `voicemail`, `disposition_timeout`, `dnc_blocked`, or `manual` for an exclusion you made. |
| `stopped_by_disposition_id` | uuid \| null      | The outcome that triggered the stop, kept even if that disposition is later deleted.                                                                            |
| `attempt_count`             | integer           | Dial attempts consumed, against `max_attempts`.                                                                                                                 |
| `infra_retries_used`        | integer           | Retries spent on failures that never reached the person. These do not consume a dial attempt.                                                                   |
| `next_attempt_at`           | timestamp         | When this contact becomes eligible again.                                                                                                                       |
| `last_disposition_id`       | uuid \| null      | Most recent outcome.                                                                                                                                            |
| `last_disconnect_reason`    | string \| null    | How the last call ended, when the carrier reported a reason.                                                                                                    |
| `last_status_at`            | timestamp \| null | When the state last changed.                                                                                                                                    |
| `completed_at`              | timestamp \| null | When this contact reached a terminal state.                                                                                                                     |
| `created_at`                | timestamp         | When the contact was enrolled.                                                                                                                                  |
| `lead`                      | object            | The full lead record.                                                                                                                                           |
| `last_disposition`          | object \| null    | The full disposition record for `last_disposition_id`.                                                                                                          |

## Example request

```bash theme={null}
# Everything, first page
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads" \
  -H "Authorization: Bearer $YAPPR_API_KEY"

# Only contacts that finished without being reached
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads?status=exhausted,completed_failed&limit=200" \
  -H "Authorization: Bearer $YAPPR_API_KEY"

# What is live right now
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads?status=dialing,awaiting_disposition" \
  -H "Authorization: Bearer $YAPPR_API_KEY"
```

## Example response

```json theme={null}
{
  "data": [
    {
      "id": "c7d2e1f0-0000-4a55-8b12-000000000031",
      "lead_id": "a1c3e5f7-0000-4b21-9c30-000000000021",
      "to_number_e164": "+972501234567",
      "status": "completed_success",
      "stop_hit": true,
      "stop_reason": "stop_disposition",
      "stopped_by_disposition_id": "5c1d9a2e-0000-4b10-9f31-000000000011",
      "attempt_count": 2,
      "infra_retries_used": 0,
      "next_attempt_at": "2026-07-27T08:12:00.000Z",
      "last_disposition_id": "5c1d9a2e-0000-4b10-9f31-000000000011",
      "last_disconnect_reason": "Hangup",
      "last_status_at": "2026-07-27T08:41:12.220Z",
      "completed_at": "2026-07-27T08:41:12.220Z",
      "created_at": "2026-07-26T09:20:01.004Z",
      "lead": {
        "id": "a1c3e5f7-0000-4b21-9c30-000000000021",
        "phone_number": "+972501234567",
        "name": "Dana Levi",
        "email": "dana@example.com",
        "source": "api"
      },
      "last_disposition": {
        "id": "5c1d9a2e-0000-4b10-9f31-000000000011",
        "label": "Not Interested",
        "color": "#f97316"
      }
    },
    {
      "id": "c7d2e1f0-0000-4a55-8b12-000000000032",
      "lead_id": "a1c3e5f7-0000-4b21-9c30-000000000022",
      "to_number_e164": "+972521112222",
      "status": "scheduled",
      "stop_hit": false,
      "stop_reason": null,
      "stopped_by_disposition_id": null,
      "attempt_count": 1,
      "infra_retries_used": 1,
      "next_attempt_at": "2026-07-28T12:03:00.000Z",
      "last_disposition_id": null,
      "last_disconnect_reason": "No Answer",
      "last_status_at": "2026-07-28T12:02:00.140Z",
      "completed_at": null,
      "created_at": "2026-07-26T09:20:01.004Z",
      "lead": {
        "id": "a1c3e5f7-0000-4b21-9c30-000000000022",
        "phone_number": "+972521112222",
        "name": "Yossi Mor",
        "email": null,
        "source": "api"
      },
      "last_disposition": null
    }
  ],
  "pagination": { "total": 412, "limit": 50, "offset": 0 },
  "campaign_id": "b3f1c0d2-5a44-4f0e-9c11-7a2e8d3f0001",
  "company_id": "fe493f11-0000-0000-0000-000000000001"
}
```

`lead` and `last_disposition` are abbreviated above; in a real response each is the complete object.

`last_disposition` is `null` while a call is still being categorized, and stays `null` for calls that never connected — a `last_disconnect_reason` with no disposition is the normal shape for an unanswered call.

## Errors

| HTTP | Code                 | When                                                                 |
| ---- | -------------------- | -------------------------------------------------------------------- |
| 401  | `INSUFFICIENT_SCOPE` | API key lacks `campaigns:read`.                                      |
| 404  | —                    | No campaign with that ID in this workspace, or it has been archived. |
| 500  | —                    | The list query failed. Safe to retry.                                |


## OpenAPI

````yaml GET /campaigns/{id}/leads
openapi: 3.1.0
info:
  title: Yappr API
  description: >
    Create and manage AI voice agents, purchase phone numbers, configure tools,
    and initiate calls — all via REST.
  version: 1.0.0
  contact:
    url: https://goyappr.com
servers:
  - url: https://api.goyappr.com
    description: Production
security:
  - apiKey: []
paths:
  /campaigns/{id}/leads:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
          format: uuid
    get:
      tags:
        - Campaigns
      summary: List campaign contacts
      description: >
        Enrolled contacts and their per-contact state, oldest enrollment first.
        Required

        scope `campaigns:read`.
      operationId: listCampaignContacts
      parameters:
        - in: query
          name: status
          schema:
            type: string
          description: Comma-separated list of contact statuses to include.
          example: pending,awaiting_disposition
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
            maximum: 200
        - in: query
          name: offset
          schema:
            type: integer
            default: 0
      responses:
        '200':
          description: Enrolled contacts
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CampaignContact'
                  pagination:
                    type: object
                    properties:
                      total:
                        type: integer
                      limit:
                        type: integer
                      offset:
                        type: integer
                  campaign_id:
                    type: string
                    format: uuid
                  company_id:
                    type: string
                    format: uuid
        '404':
          description: Campaign not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    CampaignContact:
      type: object
      description: >
        One contact enrolled in a campaign — the per-contact state machine. Each
        row

        binds a `Lead` to the E.164 number snapshotted at enrollment, so later
        edits to

        the lead cannot change which number gets dialed.


        A number can only be dialed by one active campaign at a time; enrolling
        it

        elsewhere returns `409 ALREADY_IN_ACTIVE_CAMPAIGN`.
      properties:
        id:
          type: string
          format: uuid
        lead_id:
          type: string
          format: uuid
        lead:
          nullable: true
          description: Full Lead object for `lead_id`.
          allOf:
            - $ref: '#/components/schemas/Lead'
        to_number_e164:
          type: string
          example: '+972501234567'
          description: Number snapshotted at enrollment. This is what gets dialed.
        status:
          type: string
          enum:
            - pending
            - scheduled
            - dialing
            - awaiting_disposition
            - completed_success
            - completed_failed
            - exhausted
            - excluded
            - dnc
          description: >
            `pending` — eligible, waiting for the pacer.

            `scheduled` — waiting for `next_attempt_at`.

            `dialing` — a call is live.

            `awaiting_disposition` — the call ended and classification hasn't
            arrived yet.

            Contacts are never redialed from this state.

            `completed_success` — retired on a stop disposition or a human
            connect.

            `completed_failed` — retired after a terminal failure.

            `exhausted` — `max_attempts` reached without a stop condition.

            `excluded` — removed by `DELETE /campaigns/{id}/leads/{lead_id}`, or
            retired when

            the campaign was stopped or archived. Terminal.

            `dnc` — the number is on the do-not-call list.
        stop_hit:
          type: boolean
          description: >-
            True once a stop condition fired. A contact with `stop_hit` is never
            dialed again by this campaign.
        stop_reason:
          type: string
          nullable: true
          description: >-
            Why the contact was retired (e.g. `manual`, `disposition`,
            `attempts_exhausted`).
        stopped_by_disposition_id:
          type: string
          format: uuid
          nullable: true
          description: >-
            Disposition that triggered the stop. Kept even if that disposition
            is later deleted.
        attempt_count:
          type: integer
          description: Dial attempts consumed against `max_attempts`.
        infra_retries_used:
          type: integer
          description: >-
            Platform-side retries consumed against `max_infra_retries`. These do
            not count as attempts.
        next_attempt_at:
          type: string
          format: date-time
          nullable: true
        last_disposition_id:
          type: string
          format: uuid
          nullable: true
        last_disposition:
          nullable: true
          description: Full Disposition object for `last_disposition_id`.
          allOf:
            - $ref: '#/components/schemas/Disposition'
        last_disconnect_reason:
          type: string
          nullable: true
          description: User-friendly reason the last call ended.
        last_status_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
          description: When the contact was enrolled.
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
    Lead:
      type: object
      properties:
        id:
          type: string
          format: uuid
        phone_number:
          type: string
          example: '+972501234567'
        name:
          type: string
          nullable: true
        email:
          type: string
          format: email
          nullable: true
        source:
          type: string
          example: api
          description: >-
            How the lead was created: 'api', 'manual', or 'auto' (from inbound
            call).
        tags:
          type: array
          items:
            $ref: '#/components/schemas/LeadTag'
        long_term_context:
          type: string
          nullable: true
          description: >-
            AI memory for this lead. Injected into the agent's system prompt at
            call time when lead_memory_enabled is true on the agent.
        metadata:
          type: object
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Disposition:
      type: object
      properties:
        id:
          type: string
          format: uuid
        label:
          type: string
        color:
          type: string
          nullable: true
          example: '#22c55e'
        position:
          type: integer
        is_protected:
          type: boolean
          description: Protected dispositions cannot be deleted.
        created_at:
          type: string
          format: date-time
    LeadTag:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
          nullable: true
        color:
          type: string
          nullable: true
        sort_order:
          type: integer
        created_at:
          type: string
          format: date-time
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >-
        Your Yappr API key (e.g. `ypr_live_...`). Generate one in the dashboard
        under **Settings → API Keys**.

````