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

# List campaign enrollments

> One row per campaign enrollment (`workflow_campaign_audience`): every member of a campaign, whether or not Avoca ever dialed or texted them, keyset-paginated on enrollment time, oldest first. Use it for text-only campaigns such as unsold-estimate (Rehash) outreach, which `campaign-customers` returns nothing for because its grain is the dialed customer. Each row carries the enrollment status and lifecycle timestamps, the customer, `lead_id` (joins the v1 leads feed), and the CRM job the audience row was built from (`job_id`, `job_number`, `job_type`, `business_unit`). The window applies to `enrolled_at`; row contents change as the campaign works the member, so re-sweep and upsert by `id`, comparing `updated_at` to the previous sweep's `meta.as_of`. Always scoped to the team in the URL path. Requires the `read:outbound` permission.



## OpenAPI

````yaml /api-reference/openapi.json get /api/v1/teams/{teamId}/outbound/campaign-enrollments
openapi: 3.1.0
info:
  title: Avoca Enterprise API
  description: >-
    Read-only programmatic access to Avoca data: calls and recordings,
    transcripts, leads, coach evaluations, Simple Scheduler sessions and
    analytics, outbound texting activity, enterprise support tickets, and
    customer survey results.
  version: 1.5.0
servers:
  - url: https://enterprise-api.avoca.ai
security:
  - bearerAuth: []
tags:
  - name: Calls
    description: Call records, recordings, transcripts, and pre-call transfers
  - name: Leads
    description: Unified leads (v0) and the canonical leads feed (v1)
  - name: Teams
    description: Teams accessible to the API key
  - name: Coach
    description: Coach (QA) call evaluations and rubrics
  - name: Scheduler Analytics (v0)
    description: Simple Scheduler analytics — legacy flat endpoints
  - name: Sessions & Bookings
    description: v1 scheduling-widget sessions and completed bookings
  - name: Analytics (v1)
    description: v1 funnel, UTM, service-area, and issue-type analytics
  - name: Outbound Texting
    description: Campaign text and subscription-event feeds for BI ingestion
  - name: Support Tickets
    description: Customer-safe enterprise support-ticket feed
  - name: Surveys
    description: >-
      Post-call customer survey results (CSAT, resolution, feedback) for BI
      ingestion
paths:
  /api/v1/teams/{teamId}/outbound/campaign-enrollments:
    get:
      tags:
        - Outbound Texting
      summary: List campaign enrollments
      description: >-
        One row per campaign enrollment (`workflow_campaign_audience`): every
        member of a campaign, whether or not Avoca ever dialed or texted them,
        keyset-paginated on enrollment time, oldest first. Use it for text-only
        campaigns such as unsold-estimate (Rehash) outreach, which
        `campaign-customers` returns nothing for because its grain is the dialed
        customer. Each row carries the enrollment status and lifecycle
        timestamps, the customer, `lead_id` (joins the v1 leads feed), and the
        CRM job the audience row was built from (`job_id`, `job_number`,
        `job_type`, `business_unit`). The window applies to `enrolled_at`; row
        contents change as the campaign works the member, so re-sweep and upsert
        by `id`, comparing `updated_at` to the previous sweep's `meta.as_of`.
        Always scoped to the team in the URL path. Requires the `read:outbound`
        permission.
      operationId: listOutboundCampaignEnrollments
      parameters:
        - $ref: '#/components/parameters/teamIdPath'
        - name: start_date
          in: query
          required: true
          description: Include enrollments created on/after this ISO 8601 timestamp.
          schema:
            type: string
            format: date-time
        - name: end_date
          in: query
          required: true
          description: Include enrollments created before this ISO 8601 timestamp.
          schema:
            type: string
            format: date-time
        - name: campaign_status
          in: query
          required: false
          description: >-
            Comma-separated campaign statuses (default
            `ACTIVE,DRAINING,SCHEDULED,COMPLETED`).
          schema:
            type: string
            default: ACTIVE,DRAINING,SCHEDULED,COMPLETED
        - name: campaign_id
          in: query
          required: false
          description: >-
            Comma-separated campaign ids to narrow to (max 200). Discover ids
            with the campaigns endpoint.
          schema:
            type: string
        - name: campaign_type
          in: query
          required: false
          description: >-
            Comma-separated workflow types to narrow to, e.g. `UNSOLD_ESTIMATES`
            (max 20).
          schema:
            type: string
        - $ref: '#/components/parameters/cursor'
        - $ref: '#/components/parameters/limit500default50'
      responses:
        '200':
          description: Paginated campaign enrollments
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CampaignEnrollment'
                  pagination:
                    $ref: '#/components/schemas/CursorPagination'
                  meta:
                    type: object
                    properties:
                      team_id:
                        type: integer
                      start_date:
                        type: string
                        format: date-time
                      end_date:
                        type: string
                        format: date-time
                      campaign_status:
                        type: array
                        items:
                          type: string
                        description: The effective status filter
                      campaign_id:
                        type: array
                        items:
                          type: integer
                        description: Present when `campaign_id` narrowed the request
                      campaign_type:
                        type: array
                        items:
                          type: string
                        description: Present when `campaign_type` narrowed the request
                      campaign_count:
                        type: integer
                        description: Campaigns the filters resolved to
                      truncated_campaigns:
                        type: boolean
                        description: >-
                          True when the filters matched more than 500 campaigns;
                          narrow with `campaign_status`, `campaign_type`, or
                          `campaign_id`
                      as_of:
                        type: string
                        format: date-time
                        description: >-
                          UTC watermark taken before the page was read; compare
                          row `updated_at` against the previous sweep's value
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
components:
  parameters:
    teamIdPath:
      name: teamId
      in: path
      required: true
      description: The team ID
      schema:
        type: integer
    cursor:
      name: cursor
      in: query
      description: >-
        Opaque pagination cursor from the previous response's
        `pagination.cursor`
      schema:
        type: string
    limit500default50:
      name: limit
      in: query
      description: Results per page (default 50, max 500)
      schema:
        type: integer
        default: 50
        maximum: 500
  schemas:
    CampaignEnrollment:
      type: object
      description: >-
        One campaign enrollment (`workflow_campaign_audience` row). A person
        enrolled in two campaigns returns two rows, distinguished by
        `campaign_id`.
      properties:
        id:
          type: integer
          description: The enrollment id; stable across sweeps, upsert on it
        campaign_id:
          type: integer
        campaign_name:
          type:
            - string
            - 'null'
        campaign_status:
          type: string
          description: Raw campaign status
          enum:
            - CREATED
            - SCHEDULED
            - ACTIVE
            - DRAINING
            - PAUSED
            - COMPLETED
        campaign_type:
          type:
            - string
            - 'null'
          description: >-
            The campaign's workflow type (e.g. `SPEED_TO_LEAD`,
            `UNSOLD_ESTIMATES`)
        enrolled_at:
          type: string
          format: date-time
          description: >-
            When the customer entered this campaign; the window and the keyset
            apply to it
        audience_status:
          type: string
          description: >-
            Raw campaign-audience status as the dashboard audience list shows it
            (`PENDING`, `IN_PROGRESS`, `COMPLETED`, `FAILED`, `PAUSED`, …). The
            set may grow; tolerate unknown values.
        in_progress_at:
          type:
            - string
            - 'null'
          format: date-time
        completed_at:
          type:
            - string
            - 'null'
          format: date-time
        failed_at:
          type:
            - string
            - 'null'
          format: date-time
        paused_at:
          type:
            - string
            - 'null'
          format: date-time
        last_customer_reply_at:
          type:
            - string
            - 'null'
          format: date-time
        is_booked:
          type: boolean
          description: >-
            The enrollment's stored booking state: set when the campaign's own
            outreach booked a job, and also when a team member removed the
            member from the campaign as booked outside Avoca. Not by itself
            proof of an Avoca booking; use the leads feed's
            `bookings[].booked_by` for attribution.
        booked_at:
          type:
            - string
            - 'null'
          format: date-time
        customer_id:
          type:
            - string
            - 'null'
          description: CRM customer identifier; null for CSV-imported audiences
        customer_name:
          type:
            - string
            - 'null'
        customer_phone_number:
          type:
            - string
            - 'null'
          description: >-
            The reliable identity key; joins `campaign-customers` and
            `campaign-texts`
        lead_id:
          type:
            - integer
            - 'null'
          description: >-
            The v1 leads feed row this enrollment came from; null for CRM-pulled
            and CSV audiences
        job_id:
          type:
            - integer
            - 'null'
          description: >-
            The CRM job the audience row was built from (for an unsold-estimate
            campaign, the job the open estimate belongs to); null when the
            audience was not job-based
        job_number:
          type:
            - string
            - 'null'
          description: The CRM's display number for `job_id`
        job_type:
          type:
            - string
            - 'null'
        business_unit:
          type:
            - string
            - 'null'
        updated_at:
          type: string
          format: date-time
          description: >-
            Greatest of the enrollment row's and its audience-list row's update
            times and every lifecycle, booking and reply timestamp on the row;
            compare to the previous sweep's `meta.as_of`
    CursorPagination:
      type: object
      properties:
        cursor:
          type:
            - string
            - 'null'
          description: >-
            Pass back as the `cursor` query parameter to fetch the next page;
            `null` on the last page
        has_more:
          type: boolean
        limit:
          type: integer
    Error:
      type: object
      properties:
        error:
          type: string
          description: Error type
        message:
          type: string
          description: Human-readable description (newer endpoints)
      required:
        - error
  responses:
    BadRequest:
      description: Invalid parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: API key lacks the required permission or team access
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: API key in the format `avoca_<64 hex characters>`

````