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

# Get Campaign Contacts

> List contacts in a campaign with assignment and status info

# Get Campaign Contacts

Retrieve all contacts in a campaign with their current status and assignment details. For non-admin users, only threads assigned to them are returned.

## Endpoint

```
GET /v1/campaigns/{campaign_id}/contacts
```

## Authentication

<ParamField header="X-API-Key" type="string" required>
  Your API key
</ParamField>

## Path Parameters

<ParamField path="campaign_id" type="string" required>
  The campaign ID
</ParamField>

## Query Parameters

<ParamField query="user_id" type="string">
  The current user's `unique_id`. Used for filtering threads by assignment when `is_admin` is `false`.
</ParamField>

<ParamField query="is_admin" type="boolean" default="false">
  If `true`, returns all contacts in the campaign. If `false`, only returns contacts assigned to the specified `user_id`.
</ParamField>

<ParamField query="limit" type="integer" default="1000">
  Maximum number of contacts to return in this response. Values greater
  than `10000` are silently capped at `10000`.
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Number of contacts to skip before the response window. Combine with
  `limit` to page through a large campaign.
</ParamField>

## Response

<ResponseField name="success" type="boolean">
  Whether the request was successful
</ResponseField>

<ResponseField name="contacts" type="array">
  Array of contact objects

  <Expandable title="contact properties">
    <ResponseField name="thread_id" type="string">
      Thread identifier (format: `{phone_number}_{campaign_id}`)
    </ResponseField>

    <ResponseField name="phone_number" type="string">
      Contact's phone number
    </ResponseField>

    <ResponseField name="first_name" type="string">
      Contact's first name
    </ResponseField>

    <ResponseField name="last_name" type="string">
      Contact's last name
    </ResponseField>

    <ResponseField name="email" type="string">
      Contact's email address
    </ResponseField>

    <ResponseField name="has_ever_responded" type="string">
      `"true"` if the contact has replied to any message, `"false"` otherwise
    </ResponseField>

    <ResponseField name="last_message_timestamp" type="string">
      ISO 8601 timestamp of the most recent message in the thread
    </ResponseField>

    <ResponseField name="drip_step_index" type="number">
      Current drip sequence step (0-based)
    </ResponseField>

    <ResponseField name="lead_outcome" type="string">
      Lead qualification status. Values: `"qualified"`, `"not_interested"`, `"dnc"`, or empty
    </ResponseField>

    <ResponseField name="objective_met" type="string">
      `"true"` if the campaign objective was met for this contact
    </ResponseField>

    <ResponseField name="assigned_user_id" type="string">
      The `unique_id` of the user this thread is assigned to (empty if unassigned)
    </ResponseField>

    <ResponseField name="assigned_user_name" type="string">
      Display name of the assigned user (empty if unassigned)
    </ResponseField>

    <ResponseField name="assigned_at" type="string">
      ISO 8601 timestamp of when the thread was assigned (empty if unassigned)
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total" type="number">
  Total number of contacts in the campaign that match the scope filter
  (before `limit`/`offset` are applied)
</ResponseField>

<ResponseField name="returned" type="number">
  Number of contacts included in this response (after `limit`/`offset`)
</ResponseField>

<ResponseField name="limit" type="number">
  The effective `limit` used for this response (capped at `10000`)
</ResponseField>

<ResponseField name="offset" type="number">
  The `offset` used for this response
</ResponseField>

<ResponseField name="has_more" type="boolean">
  `true` when there are more contacts beyond `offset + returned`. Paginate
  by calling again with `offset = offset + returned`.
</ResponseField>

## Example Request

<CodeGroup>
  ```bash cURL (admin - all contacts) theme={null}
  curl "https://partner.teli.ai/api/proxy/v1/campaigns/camp_abc123/contacts?user_id=admin_001&is_admin=true" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```bash cURL (user - assigned only) theme={null}
  curl "https://partner.teli.ai/api/proxy/v1/campaigns/camp_abc123/contacts?user_id=user_xyz789&is_admin=false" \
    -H "X-API-Key: YOUR_API_KEY"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://partner.teli.ai/api/proxy/v1/campaigns/camp_abc123/contacts?user_id=user_xyz789&is_admin=false',
    {
      headers: {
        'X-API-Key': 'YOUR_API_KEY'
      }
    }
  );
  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://partner.teli.ai/api/proxy/v1/campaigns/camp_abc123/contacts',
      params={'user_id': 'user_xyz789', 'is_admin': 'false'},
      headers={'X-API-Key': 'YOUR_API_KEY'}
  )
  data = response.json()
  ```
</CodeGroup>

## Example Response

```json theme={null}
{
  "success": true,
  "contacts": [
    {
      "thread_id": "+15551234567_camp_abc123",
      "phone_number": "+15551234567",
      "first_name": "Anthony",
      "last_name": "Toma",
      "email": "anthony@example.com",
      "has_ever_responded": "true",
      "last_message_timestamp": "2026-02-19T17:29:00Z",
      "drip_step_index": 0,
      "lead_outcome": "qualified",
      "objective_met": "true",
      "assigned_user_id": "user_xyz789",
      "assigned_user_name": "Tao Zhang",
      "assigned_at": "2026-02-20T23:07:28Z"
    }
  ],
  "total": 1,
  "returned": 1,
  "limit": 1000,
  "offset": 0,
  "has_more": false,
  "powered_by": "Teli"
}
```

## Filtering Behavior

| Caller                | `is_admin` | Result                              |
| --------------------- | ---------- | ----------------------------------- |
| Admin                 | `true`     | All contacts in the campaign        |
| User                  | `false`    | Only contacts assigned to `user_id` |
| User (no assignments) | `false`    | Empty array                         |

## Related Endpoints

* [Assign Thread](/partner-api/threads/assign) - Assign a thread to a user
* [Unassign Thread](/partner-api/threads/unassign) - Remove a thread assignment
* [Get Campaign Messages](/partner-api/campaigns/messages) - Get conversation messages
* [Get Thread Messages](/partner-api/threads/thread-messages) - Paginated messages for a single thread
* [List Users](/partner-api/users/list) - List users available for assignment
