zoxaAI
Homepage
API ReferenceCampaigns

List contacts

GET /api/v1/campaign/{id}/contacts — the per-contact ledger, one row per contact with its full retry chain.

GET /api/v1/campaign/{campaign_id}/contacts

Return the campaign's contacts — one entry per person in the CSV, each rolled up from all of its dial attempts. When a contact is retried, every attempt lives in the same chain (root attempt + retry children), so this endpoint gives you the whole story: what happened on each try, the current rolled-up status, how many attempts were used, and when the next retry is due.

A contact is one root attempt (parent_queued_run_id IS NULL) plus its retry children, grouped by COALESCE(root_queued_run_id, id). The list paginates over contacts (roots), oldest-first (ascending id — roughly CSV order).

Query parameters

ParameterTypeDefaultNotes
pageint (≥ 1)1Page number.
limitint (1..200)50Contacts per page.
statusstring—Filter to contacts whose rolled-up status matches. One of the contact statuses.
searchstring (≤ 32)—Partial match on the contact's phone_number.

How pagination interacts with the status filter

total counts all contacts matching search, ignoring status. The status filter is applied to the current page only, after rollup — so a filtered page may contain fewer than limit items, and total is not the count of matches for that status. Page through all contacts and filter client-side if you need an exact per-status count.

Contact status

The rolled-up status of a contact is the lifecycle state of its latest attempt (or completed if any attempt in the chain completed):

StatusMeaning
pendingQueued, not yet dialed.
dialingClaimed by a batch, dialing now.
in_callDispatched; the call is in flight (no terminal outcome yet).
retry_scheduledA retry is queued for a future time.
completedThe call completed (a human was reached and the call ran to its end).
busyLine was busy on the last attempt.
no_answerNo answer on the last attempt.
voicemailReached voicemail on the last attempt.
failedTerminal failure (or dispatch failed) with no successful connection.
canceledThe call was canceled.

Response

{
  "items": [
    {
      "root_queued_run_id": 8801,
      "phone_number": "+14155552671",
      "context_variables": {
        "phone_number": "+14155552671",
        "first_name": "Sarah",
        "account_id": "ACC-1234"
      },
      "status": "completed",
      "latest_outcome": "completed",
      "attempts_used": 2,
      "next_retry_at": null,
      "last_outcome_at": "2026-07-19T14:32:07Z",
      "attempts": [
        {
          "queued_run_id": 8801,
          "retry_count": 0,
          "state": "processed",
          "status": "no_answer",
          "call_outcome": "no_answer",
          "retry_reason": null,
          "scheduled_for": null,
          "outcome_at": "2026-07-19T14:10:41Z",
          "processed_at": "2026-07-19T14:10:12Z"
        },
        {
          "queued_run_id": 8845,
          "retry_count": 1,
          "state": "processed",
          "status": "completed",
          "call_outcome": "completed",
          "retry_reason": "no_answer",
          "scheduled_for": "2026-07-19T14:30:41Z",
          "outcome_at": "2026-07-19T14:32:07Z",
          "processed_at": "2026-07-19T14:30:55Z"
        }
      ]
    }
  ],
  "total": 500,
  "page": 1,
  "limit": 50
}

Contact fields

FieldTypeNotes
root_queued_run_idintId of the chain's root attempt — a stable per-contact identifier.
phone_numberstring | nullThe contact's number (from the CSV phone_number column).
context_variablesobjectThe full CSV row for this contact (all columns, including template variables).
statusstringRolled-up contact status.
latest_outcomestring | nullcall_outcome of the latest attempt, or null if it has no terminal outcome yet.
attempts_usedintNumber of dial attempts so far (max(retry_count) + 1).
next_retry_atstring | nullWhen the next scheduled retry fires, if one is pending.
last_outcome_atstring | nullWhen the latest attempt's outcome was recorded.
attemptsarrayEvery attempt in the chain, oldest first (by retry_count).

Attempt fields

FieldTypeNotes
queued_run_idintId of this attempt row.
retry_countint0 for the root attempt; increments per retry.
statestringLedger state: queued, processing, processed, failed.
statusstringPer-attempt lifecycle status (same vocabulary as contact status).
call_outcomestring | nullTerminal call outcome: completed, busy, no_answer, voicemail, failed, canceled; null until the call terminates.
retry_reasonstring | nullWhy this retry was scheduled (busy / no_answer / voicemail). null on the root attempt.
scheduled_forstring | nullFor retries, when the attempt was scheduled to dial.
outcome_atstring | nullWhen the terminal outcome landed.
processed_atstring | nullWhen the attempt was dispatched.

Examples

# First page of contacts
curl "https://dashboard.zoxa.ai/api/v1/campaign/7/contacts?page=1&limit=50" \
  -H "X-API-Key: zsk_..."

# Only contacts currently waiting on a scheduled retry
curl "https://dashboard.zoxa.ai/api/v1/campaign/7/contacts?status=retry_scheduled" \
  -H "X-API-Key: zsk_..."

# Find one contact by partial phone number
curl "https://dashboard.zoxa.ai/api/v1/campaign/7/contacts?search=5552671" \
  -H "X-API-Key: zsk_..."

Errors

StatusdetailWhen
404"Campaign not found"Id not in your org.
422arraystatus is not one of the contact statuses, limit > 200, or search exceeds 32 characters.

On this page