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

# List a campaign's contacts

> Newest-first, cursor-paginated. `status` filters by dial status;
`disposition_id` by the contact's most recent disposition.




## OpenAPI

````yaml /openapi.yaml get /campaigns/{id}/contacts
openapi: 3.1.0
info:
  title: Dialbird Public API
  version: 1.0.0
  description: >
    The Dialbird Public API is a versioned, machine-to-machine REST surface for

    building automations on top of Dialbird (Zapier, n8n, Make, custom scripts).


    ## Authentication


    All endpoints except `GET /health` require an OAuth 2.0 bearer access token

    obtained via the Authorization Code flow against Dialbird's OIDC provider.

    The token is a signed JWT with `aud: "public-api"`; the API verifies it

    against the issuer's JWKS. Send it on every request:


    ```

    Authorization: Bearer <access_token>

    ```


    Access tokens must **never** be passed as a `?access_token=` query parameter
    —

    requests that do are rejected with `401 invalid_token`.


    ## Request & response conventions


    - All request and response bodies are JSON (`application/json`).

    - Field names are `snake_case`.

    - Every response carries an `X-Request-Id` header. Include it when
    contacting
      support. Clients may supply their own via the `X-Request-Id` request header
      (`^[A-Za-z0-9_-]{1,64}$`); otherwise the API generates one.
    - Phone numbers are always E.164 (e.g. `+15551234567`).


    ## Idempotency


    Write endpoints (`POST /contacts`, `POST /messages`) accept an optional

    `Idempotency-Key` header (≤255 chars). Retrying a request with the same key

    and identical body replays the original response without re-running the

    operation. Reusing a key with a *different* body returns `409

    idempotency_key_reused`; a retry while the first request is still in flight

    returns `409` with code `idempotency_in_progress`. Keys are retained for
    24h.


    ## Rate limiting


    Authenticated requests are limited per `(business, oauth_client)`; anonymous

    requests (only `GET /health`) are limited per IP. Every response includes:


    - `X-RateLimit-Limit` — requests allowed in the current window

    - `X-RateLimit-Remaining` — requests left in the window

    - `X-RateLimit-Reset` — unix epoch seconds when the window resets


    A `429 rate_limited` response additionally carries a `Retry-After` header

    (seconds).


    ## Errors


    Errors share one envelope:


    ```json

    { "error": { "code": "not_found", "message": "…", "field": "to",
    "request_id": "req_…" } }

    ```


    `field` is present only for validation errors that map to a specific input.
  contact:
    name: Dialbird Support
    email: team@usechalkboard.com
servers:
  - url: '{origin}/api/v1'
    description: Dialbird Public API base URL
    variables:
      origin:
        default: https://app-staging.dialbird.io
        enum:
          - https://app-staging.dialbird.io
        description: >-
          The Dialbird deployment origin (matches the OIDC issuer host).
          Defaults to staging.
security:
  - OAuth2: []
tags:
  - name: System
    description: Unauthenticated health checks.
  - name: Identity
    description: The authenticated principal.
  - name: Contacts
    description: Create, update, and read contacts.
  - name: Conversations
    description: Read conversations and their messages.
  - name: Messages
    description: Send SMS messages.
  - name: Campaigns
    description: |
      Dialer campaigns and their contacts, and the dispositions (call outcomes)
      the dialer records. Needs the dialer on the workspace's plan; otherwise
      every campaign endpoint returns `403 forbidden`.
  - name: Webhooks
    description: Manage REST-Hook webhook subscriptions.
paths:
  /campaigns/{id}/contacts:
    get:
      tags:
        - Campaigns
      summary: List a campaign's contacts
      description: |
        Newest-first, cursor-paginated. `status` filters by dial status;
        `disposition_id` by the contact's most recent disposition.
      operationId: listCampaignContacts
      parameters:
        - $ref: '#/components/parameters/CampaignId'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - name: status
          in: query
          schema:
            $ref: '#/components/schemas/CampaignContactStatus'
        - name: disposition_id
          in: query
          schema:
            type: string
      responses:
        '200':
          description: One page of campaign contacts.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - next_cursor
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CampaignContact'
                  next_cursor:
                    type:
                      - string
                      - 'null'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - OAuth2:
            - api:campaigns:read
components:
  parameters:
    CampaignId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: The campaign id.
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      schema:
        type: string
      description: Opaque cursor from a previous response's `next_cursor`.
  schemas:
    CampaignContactStatus:
      type: string
      enum:
        - not_called
        - in_progress
        - called
        - completed
        - do_not_call
      description: |
        `completed` once a disposition that completes the contact is picked;
        `do_not_call` contacts are never dialed.
    CampaignContact:
      type: object
      properties:
        id:
          type: string
        campaign_id:
          type: string
        first_name:
          type:
            - string
            - 'null'
        last_name:
          type:
            - string
            - 'null'
        company:
          type:
            - string
            - 'null'
        title:
          type:
            - string
            - 'null'
        email:
          type:
            - string
            - 'null'
        linkedin_url:
          type:
            - string
            - 'null'
        phones:
          type: array
          description: In dial order, E.164.
          items:
            type: object
            properties:
              number:
                type: string
                example: '+15551234567'
              label:
                type:
                  - string
                  - 'null'
        primary_number:
          type: string
        status:
          $ref: '#/components/schemas/CampaignContactStatus'
        call_count:
          type: integer
        last_called_at:
          type:
            - string
            - 'null'
          format: date-time
        last_disposition:
          oneOf:
            - $ref: '#/components/schemas/Disposition'
            - type: 'null'
        extra_fields:
          type:
            - object
            - 'null'
          additionalProperties:
            type: string
        customer_id:
          type:
            - string
            - 'null'
          description: >-
            The saved Dialbird contact this campaign contact is linked to, if
            any.
        due_at:
          type:
            - string
            - 'null'
          format: date-time
        created_at:
          type: string
          format: date-time
    Disposition:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          example: Meeting set
        source:
          type: string
          enum:
            - dialbird
            - hubspot
          description: >-
            The list it belongs to - the workspace's own, or HubSpot's call
            outcomes.
        system_key:
          type:
            - string
            - 'null'
          description: >-
            Set on the dispositions the dialer records by itself (e.g. no
            answer, voicemail).
        marks_contact_complete:
          type: boolean
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
            field:
              type: string
              description: >-
                Present only for validation errors tied to a specific input
                field.
            request_id:
              type: string
    ErrorCode:
      type: string
      enum:
        - validation_error
        - invalid_phone_number
        - invalid_event_type
        - missing_token
        - invalid_token
        - expired_token
        - insufficient_scope
        - forbidden
        - business_suspended
        - not_found
        - conflict
        - idempotency_key_reused
        - idempotency_in_progress
        - gone
        - unprocessable_entity
        - rate_limited
        - internal_error
  headers:
    RequestId:
      description: Echoes the request id (client-supplied or generated).
      schema:
        type: string
    WWWAuthenticate:
      description: Bearer challenge.
      schema:
        type: string
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
    RateLimitLimit:
      description: Requests allowed in the current window.
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
    RateLimitReset:
      description: Unix epoch seconds when the window resets.
      schema:
        type: integer
  responses:
    ValidationError:
      description: The request body failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing, malformed, invalid, or expired token.
      headers:
        WWW-Authenticate:
          $ref: '#/components/headers/WWWAuthenticate'
        X-Request-Id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: >-
        Token lacks the required scope, or the business is suspended / not
        associated.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Resource not found.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Rate limit exceeded.
      headers:
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    OAuth2:
      type: oauth2
      description: |
        Authorization Code flow against Dialbird's OIDC provider. The issued
        access token is a JWT with `aud: "public-api"`, verified against the
        issuer JWKS.
      flows:
        authorizationCode:
          authorizationUrl: https://app-staging.dialbird.io/oidc/auth
          tokenUrl: https://app-staging.dialbird.io/oidc/token
          refreshUrl: https://app-staging.dialbird.io/oidc/token
          scopes:
            api:me: Read the authenticated business, user, and granted scopes.
            api:contacts:read: Read contacts.
            api:contacts:write: Create and update contacts.
            api:messages:read: Read messages.
            api:messages:write: Send messages.
            api:calls:read: Read calls.
            api:campaigns:read: Read campaigns, their contacts, and dispositions.
            api:campaigns:write: Create campaigns and add, update, and remove their contacts.
            api:webhooks: Manage webhook subscriptions.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.