zoxaAI
Homepage
API ReferenceCampaigns

Campaigns overview

How campaigns dispatch bulk outbound calls, manage concurrency, isolate phone-number pools per telephony config, and surface structured event logs.

A campaign is a managed bulk-outbound batch: a CSV of contacts + a target agent + a telephony configuration + per-campaign concurrency, retry, scheduling, and circuit-breaker rules. zoxaAI dispatches calls, respects all the limits, and records every important state transition as a structured logs[] entry on the campaign — you call start, then poll progress / runs (or read logs[]) for visibility.

Lifecycle

created → syncing → running → (paused | completed | failed)
                       ↑              ↓
                       └── resume ─────┘

pause works only while running. resume works from either paused or failed — both are recoverable, and resume revalidates wallet + telephony first. Transient trouble while dialing (repeated batch errors, a drained phone-number pool) pauses the campaign, so failed is reserved for unrecoverable setup errors (e.g. a CSV sync failure). Completion is exact: a campaign moves to completed the moment its last call's outcome lands.

Endpoints

MethodPathPurpose
POST/campaign/createCreate a campaign tied to an agent + a CSV source + (optionally) a telephony config.
GET/campaignList campaigns in your org (paginated; state / archived filters).
GET/campaign/{id}Single campaign detail + stats + logs[].
PATCH/campaign/{id}Update name, concurrency, retry/schedule/circuit-breaker, and telephony config (before running).
POST/campaign/{id}/startBegin dispatching.
POST/campaign/{id}/pausePause dispatch (in-flight calls finish).
POST/campaign/{id}/resumeResume a paused or failed campaign.
POST/campaign/{id}/archiveHide a campaign (reversible, never deletes).
POST/campaign/{id}/unarchiveRestore an archived campaign.
GET/campaign/{id}/progressReal-time progress counters.
GET/campaign/{id}/contactsPer-contact ledger with retry chains.
GET/campaign/{id}/insightsFunnel, outcomes, retry effectiveness, cost/duration, timeline.
GET/campaign/{id}/runsPaginated per-call results.
WS/campaign/{id}/eventsLive event stream for a campaign.
GET/campaign/{id}/source-download-urlSigned URL to download the original CSV.

Target agent

Every campaign targets one agent. Pass the agent's public uuid as agent_uuid when you create the campaign; each contact is called by that agent's pipeline. The campaign response reports the resolved agent as agent_id (internal integer id) and agent_name. The target can't be changed after creation.

CSV source

Campaigns are CSV-only. Contacts come from a previously-uploaded CSV file (via Files or the campaign create form). Required: a phone_number column. Optional: any number of template-variable columns — referenced from your agent prompt as {{column_name}}.

Telephony configuration

Each campaign is bound to a specific telephony configuration — the provider account and outbound phone-number pool it dials from. Pass telephony_configuration_id on create to pick one explicitly, or omit it to inherit the org's default.

From-number pool isolation

The from-number pool is keyed per (organization, telephony_configuration_id). Two campaigns running on different telephony configs draw from independent caller-ID pools — they never share or steal numbers. This isolation also drives the bounded phone-pool exhaustion retry (see Logs below).

Concurrency, retry, schedule, circuit breaker

All four are optional structured configs on create / update:

BlockControls
max_concurrencyHard ceiling on simultaneously-active calls for this campaign.
retry_configWhen + how often to retry busy / no-answer / voicemail contacts.
schedule_configTime windows (e.g. business hours, timezone).
circuit_breakerAuto-pause if failure rate spikes within a rolling window.

`max_concurrency` is capped

The effective ceiling is the lower of the org concurrency limit and the available caller-number count (from the org's default telephony config). A 400 is returned if you try to exceed it.

Structured event logs

Every campaign carries an append-only logs[] array in its API response. The orchestrator, circuit breaker, source-sync, and phone-pool-exhaustion handler all write structured entries to it — so operators can see why a campaign moved to paused or failed without trawling server logs.

Each entry has:

FieldTypeNotes
timestampISO 8601 stringWhen the event happened.
levelstringinfo / warning / error.
eventstringEvent identifier (see below).
messagestringHuman-friendly summary.
detailsobject | nullEvent-specific structured context.

Common event identifiers:

EventMeaningNotable details
circuit_breaker_trippedFailure rate crossed the configured thresholdWindow stats + recent_failures[] (last 20 failures — call id, reason, timestamp)
phone_number_pool_exhausted_retryNo free from-number available; retry attempt N of 3attempt, max_attempts
phone_number_pool_exhaustedAll 3 retries exhausted; campaign paused (recoverable)attempt, max_attempts
batch_failure_retryA batch errored; bounded retry attempt N of 3error
batch_failures_paused3 consecutive batch failures; campaign paused (recoverable)error
stuck_rows_sweptThe self-healing sweep recovered contacts a crashed worker strandedreverted, advanced
stale_call_resolvedA call whose terminal status callback never arrived was resolvedqueued_run_ids
campaign_archived / campaign_unarchivedCampaign hidden / restored—
Source-sync failuresCSV parse / schema issuesUnderlying error

Pool exhaustion and batch errors pause, not fail

Repeated batch failures and a drained phone-number pool pause the campaign (recoverable) after 3 consecutive misses, with a structured log explaining why. A paused campaign can be resumed once the underlying issue clears.

On this page