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

# The campaign's call list

> Paged, sortable and searchable, because this is what an operator reads BEFORE launching a campaign rather than a diagnostic dump afterwards. It was capped at 500 with no paging, no sort and no filter: a list of fifty thousand showed the first five hundred by state and gave no way to find anybody, which is a sample rather than a review.

Each contact carries `fields` -- the columns their uploaded row had, the same values the agent sees mid-call -- and the campaign carries `list_columns`, the file's header in its own order, so the list renders as the table that was uploaded rather than as a phone book.

A contact whose record is marked do-not-call shows as `suppressed`, so the list says why somebody will never be called rather than leaving an operator to wonder. Sort keys: attempt_count, e164, last_attempt_at, name, state.



## OpenAPI

````yaml /api-reference/openapi-public.json get /campaigns/{id}/contacts
openapi: 3.0.3
info:
  description: >-
    The API your own systems use: place and follow calls, have an AI agent call
    someone, run campaigns and get their results, keep contacts in step with
    your CRM, and receive signed webhooks. Authenticate with an integration key
    (Authorization: Bearer ft_...), used only from the IP addresses it allows.
  title: FireTone API
  version: 0.1.0
servers:
  - description: Your platform's API host
    url: https://{host}/api/v1
    variables:
      host:
        default: api.firet.one
security:
  - bearerAuth: []
tags:
  - name: Auth
  - description: >-
      Live calls and what can be done to them: hang up, hold, transfer, park,
      merge, monitor, whisper; the Desk's own call.
    name: Calls
  - description: 'Outbound campaigns: contacts, attempts, outcomes.'
    name: Campaigns
  - description: 'Customers: who called, what is known about them, and their memory.'
    name: Contacts
  - description: What was said on an AI call, and the review of it.
    name: Conversations
  - description: >-
      Your own systems: HTTP connections an IVR calls mid-call, and webhooks for
      call events. Tenant URLs must be public https addresses.
    name: Integrations
  - name: Live
  - name: Provisioning
  - name: Reporting
  - description: Tickets raised by people, agents and the API.
    name: Tickets
  - description: Messages left for an extension or a queue.
    name: Voicemail
paths:
  /campaigns/{id}/contacts:
    get:
      tags:
        - Campaigns
      summary: The campaign's call list
      description: >-
        Paged, sortable and searchable, because this is what an operator reads
        BEFORE launching a campaign rather than a diagnostic dump afterwards. It
        was capped at 500 with no paging, no sort and no filter: a list of fifty
        thousand showed the first five hundred by state and gave no way to find
        anybody, which is a sample rather than a review.


        Each contact carries `fields` -- the columns their uploaded row had, the
        same values the agent sees mid-call -- and the campaign carries
        `list_columns`, the file's header in its own order, so the list renders
        as the table that was uploaded rather than as a phone book.


        A contact whose record is marked do-not-call shows as `suppressed`, so
        the list says why somebody will never be called rather than leaving an
        operator to wonder. Sort keys: attempt_count, e164, last_attempt_at,
        name, state.
      operationId: getCampaignsIdContacts
      parameters:
        - in: path
          name: id
          required: true
          schema:
            format: uuid
            type: string
        - $ref: '#/components/parameters/limit'
        - $ref: '#/components/parameters/offset'
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/order'
        - $ref: '#/components/parameters/q'
        - in: query
          name: state
          schema:
            enum:
              - pending
              - leased
              - done
              - failed
              - suppressed
            type: string
        - in: query
          name: disposition
          schema:
            type: string
        - in: query
          name: dnc
          schema:
            type: boolean
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CampaignContactPage'
          description: OK
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    limit:
      in: query
      name: limit
      schema:
        default: 50
        maximum: 500
        type: integer
    offset:
      in: query
      name: offset
      schema:
        default: 0
        type: integer
    sort:
      description: >-
        Sort key. Each list accepts its own allow-list of keys, given in that
        endpoint's description; anything else is a 400. ORDER BY cannot be
        parameterised, so the key is looked up rather than interpolated. Every
        sort carries a tiebreak on the primary key, so paging stays disjoint
        when the sort column has duplicates.
      in: query
      name: sort
      schema:
        type: string
    order:
      description: >-
        Sort direction. Defaults to ascending, except the call log, which reads
        newest first.
      in: query
      name: order
      schema:
        enum:
          - asc
          - desc
        type: string
    q:
      description: >-
        Free-text search across the list's searchable columns (case-insensitive
        substring). % and _ in the term match themselves.
      in: query
      name: q
      schema:
        type: string
  schemas:
    CampaignContactPage:
      properties:
        items:
          items:
            properties:
              attempt_count:
                type: integer
              contact_id:
                format: uuid
                type: string
              disposition:
                type: string
              dnc:
                type: boolean
              e164:
                type: string
              fields:
                additionalProperties:
                  type: string
                description: >-
                  This contact's own data -- the columns their CSV row had. The
                  same values the agent sees mid-call, so what is reviewed
                  before launch is what will actually be used.
                type: object
              last_attempt_at:
                format: date-time
                type: string
              name:
                type: string
              next_attempt_at:
                format: date-time
                type: string
              state:
                description: >-
                  suppressed means the contact is marked do-not-call and will
                  never be dialled.
                enum:
                  - pending
                  - leased
                  - done
                  - failed
                  - suppressed
                type: string
            type: object
          type: array
        limit:
          type: integer
        offset:
          type: integer
        total:
          type: integer
      required:
        - items
        - total
      type: object
    Error:
      properties:
        error:
          properties:
            code:
              enum:
                - invalid_request
                - invalid_credentials
                - unauthenticated
                - forbidden
                - not_found
                - conflict
                - internal
              type: string
            message:
              type: string
          required:
            - code
            - message
          type: object
      required:
        - error
      type: object
  responses:
    Unauthorized:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Missing, invalid or expired token
    Forbidden:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Outside the caller's scope, or insufficient role
    NotFound:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Not found, or not visible to the caller
  securitySchemes:
    bearerAuth:
      bearerFormat: JWT or API key
      description: >-
        Every request sends `Authorization: Bearer <token>`. The token is either
        a panel session (a JWT from /auth/login, 12 hours) or an API key
        `ft_<id>_<secret>`. An API key is accepted only from an address on its
        IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required
        for an old key that has none), is limited to its rate per minute (429
        rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on
        every response), and at most 60 call placements a minute.
      scheme: bearer
      type: http

````