#Outbound Campaigns
Outbound campaign management routes are mounted under /v1/outbound-campaigns.
These routes manage partner-facing campaign records over the existing AiDial outbound runtime tables.
Production API: https://api.aidial.ai
This page omits environment-specific host names, internal topology, and deployment details.
These routes do not expose raw runtime configuration editing, imported contact phone numbers, imported CSV content, ciphertext, provider secrets, secret-backend references, API keys, bearer tokens, or transcripts. Campaign detail can include configured CLI disclosure fields such as cli_return_number because those fields are part of the campaign compliance record, not imported contact data.
#Authentication and Scope
All routes require authentication through the shared API auth layer:
X-API-KeyAuthorization: Bearer <jwt>
If both headers are present, the API key is used and the bearer token is ignored. Missing, invalid, revoked, or expired credentials return 401 Unauthorized.
Bearer authentication requires ZITADEL_ISSUER, ZITADEL_AUDIENCE, and ZITADEL_JWKS_URL; these settings are required and have no fallback defaults. Bearer tenant assignment is resolved from portal_users and portal_user_client_assignments.
Both authentication modes resolve to an AuthContext. This router scopes all campaign queries and mutations to the resolved client_id and data_env; it does not accept a client or data-environment override in the request. Outbound campaign records are read and written through the tenant data access layer using the strict (client_id, data_env) route in the tenant routing map. Authentication, idempotency, project metadata, and route lookup use the local API metadata store on the API host.
Write routes require partner_admin. Read routes allow partner_admin, partner_user, aidial_admin, and aidial_operator within the resolved authenticated scope. Out-of-scope campaigns, invalid campaign ids, and role-denied mutations return non-enumerating 404 behavior.
#Routes
| Endpoint | Method | Scope | Purpose |
|---|---|---|---|
/v1/outbound-campaigns | GET | read | List campaign summaries with project, status, call type, and search filters. |
/v1/outbound-campaigns | POST | partner_admin | Create a campaign. |
/v1/outbound-campaigns/{campaign_id} | GET | read | Read safe campaign detail. |
/v1/outbound-campaigns/{campaign_id} | PATCH | partner_admin | Update mutable campaign metadata while state allows edits. |
/v1/outbound-campaigns/{campaign_id}/contacts | GET | read | List masked contact status, outcome, retry, consent, and registry summaries. |
/v1/outbound-campaigns/{campaign_id}/contacts/import | POST | partner_admin | Import CSV contacts. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/registry-wash-records | POST | partner_admin | Attach DNCR/registry wash evidence. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/validation | GET | read | Read launch validation state. |
/v1/outbound-campaigns/{campaign_id}/validate | POST | partner_admin | Recompute and audit launch validation. |
/v1/outbound-campaigns/{campaign_id}/run | POST | partner_admin | Launch after validation passes. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/pause | POST | partner_admin | Pause campaign dispatch. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/resume | POST | partner_admin | Resume after validation passes. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/cancel | POST | partner_admin | Cancel without deleting history. Requires Idempotency-Key. |
/v1/outbound-campaigns/{campaign_id}/report | GET | read | Read reporting aggregates and validation state. |
#Query Parameters
GET /v1/outbound-campaigns supports:
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number, minimum 1. |
page_size | integer | 20 | Page size, minimum 1, maximum 100. |
sort_by | string | updated_at_utc | One of updated_at_utc, created_at_utc, name, or status. |
sort_order | string | desc | asc or desc. |
project_id | string | - | Filters campaigns to one project within the authenticated client scope. |
status | string | - | Filters by campaign status. |
campaign_call_type | string | - | Filters by telemarketing, research, or service. |
search | string | - | Case-insensitive campaign-name search, maximum 120 characters. |
GET /v1/outbound-campaigns/{campaign_id}/contacts supports:
| Name | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number, minimum 1. |
page_size | integer | 50 | Page size, minimum 1, maximum 200. |
sort_by | string | created_at_utc | One of created_at_utc, updated_at_utc, status, or external_contact_id. |
sort_order | string | asc | asc or desc. |
status | string | - | Contact status filter. |
disposition | string | - | Filters by last_disposition. |
callable | boolean | - | Filters by is_callable. |
validation_code | string | - | Filters by not_callable_reason. |
retry_due | boolean | - | true filters contacts whose next_attempt_at_utc is due. false is treated the same as omitted. |
external_contact_id | string | - | Filters by exact external contact id. |
#Request Bodies
#Create Campaign
POST /v1/outbound-campaigns accepts a JSON body with:
| Field | Type | Required | Description |
|---|---|---|---|
project_id | string | Yes | Project owned by the authenticated client. |
name | string | Yes | Campaign name, 1 to 255 characters. |
description | string or null | No | Optional description, maximum 4000 characters. |
campaign_type | string | No | single, batch, or scheduled; default is scheduled. |
campaign_call_type | string | Yes | telemarketing, research, or service. |
priority | integer | Yes | 1 to 100. |
schedule_start_at_utc | datetime or null | No | Optional UTC schedule start. |
schedule_end_at_utc | datetime or null | No | Optional UTC schedule end; must be after start when both are supplied. |
timezone | string | Yes | Campaign timezone name. |
retry_policy | object | Yes | Retry settings. |
voicemail_policy | object | Yes | Voicemail and IVR handling settings. |
personalization | object | Yes | Approved personalization keys. |
compliance | object | Yes | Campaign compliance settings. |
retry_policy contains enabled, max_attempts (0 to 20), backoff_base_seconds (0 to 604800), backoff_strategy (constant, linear, or exponential), and optional booleans retry_on_no_answer, retry_on_voicemail, and retry_on_busy.
voicemail_policy is the partner-facing name for the campaign's answering-machine handling policy. It contains detection_enabled, on_voicemail (leave_message, hangup, or retry), on_ivr (hangup or retry), on_silence (retry or hangup), on_unknown (proceed, retry, or hangup), and optional message_template. Create reads the current project runtime detector configuration from outbound_call.answering_machine_detection and persists the campaign snapshot into the current answering_machine_detection_* runtime columns. The old outbound project keys outbound_call.voicemail_detection and outbound_call.voicemail_handling are not accepted for campaign creation.
personalization.required_keys and personalization.allowed_keys must use keys of 64 characters or fewer containing only letters, numbers, hyphen, or underscore. Required keys must also be listed as allowed keys.
compliance contains registry_wash_required, registry_wash_max_age_days (1 to 365), consent_required, prevent_after_hours, check_public_holiday, recipient_timezone_aware, and optional CLI or exemption fields: cli_return_number, cli_employer_name, cli_authorizer_name, cli_call_purpose, and exemption_type.
Create loads the published project runtime configuration for the authenticated client. Missing, archived, unpublished, undeployed, or outbound-incomplete project runtime state returns a 422 validation response. A successful create returns 201 Created and the new campaign detail. Created campaigns start in draft.
#Update Campaign
PATCH /v1/outbound-campaigns/{campaign_id} accepts at least one of:
namedescriptionpriorityschedule_start_at_utcschedule_end_at_utc
Only draft, scheduled, and paused campaigns can be updated. Other states return 422 invalid_campaign_state.
#Registry Wash Record
POST /v1/outbound-campaigns/{campaign_id}/registry-wash-records accepts:
| Field | Type | Required | Description |
|---|---|---|---|
jurisdiction | string | Yes | Jurisdiction code, 2 to 10 characters. |
registry_type | string | Yes | Registry type, for example dncr. |
vendor | string or null | No | Registry vendor name. |
ruleset_id | string or null | No | Ruleset identifier. |
wash_channel | string | Yes | Wash channel. |
transaction_id | string or null | No | External transaction id. |
wash_file_name | string or null | No | External wash file name. |
numbers_submitted | integer | Yes | Total submitted numbers. |
numbers_on_register | integer | Yes | Count found on the registry. |
numbers_not_on_register | integer | Yes | Count not found on the registry. |
numbers_invalid | integer | Yes | Invalid-number count. |
credits_charged | integer | Yes | Registry credits charged. |
credits_remaining | integer or null | No | Remaining registry credits. |
washed_at_utc | datetime | Yes | Wash completion time. |
valid_until_utc | datetime | Yes | Evidence expiry time; must be after washed_at_utc. |
receipt_reference | string or null | No | External receipt reference. |
The three result counts must add up to numbers_submitted. Secret-like strings such as passwords, API keys, tokens, secret-backend paths, or PEM markers are rejected in registry metadata fields. If a transaction_id already exists for the campaign, the latest matching record is returned instead of inserting a duplicate.
#CSV Import
POST /v1/outbound-campaigns/{campaign_id}/contacts/import accepts UTF-8 CSV, with an optional UTF-8 BOM, up to 1 MB and 5000 contact rows. Contacts can only be imported into draft or paused campaigns.
Required header:
to_number
Optional headers include:
external_contact_idrecipient_timezonerecipient_countryrecipient_subdivisionregistry_statusregistry_checked_at_utcregistry_valid_until_utcregistry_result_coderegistry_vendorregistry_ruleset_idconsent_typeconsent_sourceconsent_given_at_utcconsent_expires_at_utcconsent_withdrawn_at_utcconsent_evidence_referenceexemption_typeexemption_verifiedcall_typepriority_overridemax_attempts
Campaign-approved personalization keys may also be included as columns. Unsupported columns are rejected before row processing.
to_number must be E.164. Duplicate external_contact_id values in the same CSV are rejected row by row. For persisted rows, the API stores phone numbers as a hash plus server-side ciphertext, and stores personalization as server-side ciphertext plus a hash. Raw phone numbers and plaintext personalization are not returned.
Valid rows are accepted and invalid rows return row-level errors. A CSV with any row-level validation failures, including a file where every contact row is invalid, returns an import response with accepted and rejected row counts. Header-level, encoding, and size failures return 422.
#Launch Validation
GET /v1/outbound-campaigns/{campaign_id}/validation and POST /v1/outbound-campaigns/{campaign_id}/validate compute the same launch validation result. Run and resume controls recompute validation before changing state. If validation is blocked, run and resume return the current campaign and validation result without transitioning the campaign.
Launch is blocked when required evidence or runtime prerequisites are missing, including:
- no imported contacts
- missing, stale, expired, or count-mismatched DNCR/registry wash evidence for telemarketing or research
- contacts on a registry, missing registry status, missing registry check timestamps, stale registry checks, or expired registry validity when enforcement is enabled
- missing, expired, withdrawn, or unverified consent or exemption evidence where consent is required
- missing or invalid recipient timezone, missing recipient location, after-hours, or public-holiday blockers for telemarketing or research campaigns governed by calling-window policy
- missing project runtime configuration or blocking project outbound compliance findings
- outbound capacity not configured
- missing CLI disclosure fields for telemarketing or research
Validation responses contain status (ready, blocked, or warning), blockers, warnings, info, and contact_blocker_counts.
#Responses
Campaign list responses return:
| Field | Type | Description |
|---|---|---|
items | array | Campaign summaries. |
pagination | object | page, page_size, total, and total_pages. |
Campaign summaries include campaign_id, project_id, name, description, campaign_type, campaign_call_type, status, priority, timezone, counts, validation_state, schedule timestamps, registry-wash summary fields, and created/updated timestamps.
Campaign detail responses add started_at_utc, completed_at_utc, retry_policy, voicemail_policy, personalization, compliance, and nullable last_registry_wash_record_id.
Contact list responses return items and pagination. Contact items include contact_id, external_contact_id, masked_phone, personalization_keys, status, is_callable, not_callable_reason, registry summary fields, consent and exemption summary fields, attempt and retry timestamps, last_disposition, and nullable last_call_id.
Contact import responses include accepted_rows, rejected_rows, duplicate_rows, row_errors, and the refreshed campaign detail.
Registry wash responses include registry_wash_record_id, campaign_id, jurisdiction, registry_type, wash_channel, transaction_id, submitted/on-register/not-on-register/invalid counts, washed_at_utc, and valid_until_utc.
Control responses include the campaign detail and, for run and resume, the launch validation result. Report responses include campaign-level aggregate counts by contact status, callable state, disposition, registry status, linked calls, retry-due total, empty voicemail_ivr_counts, and the current validation result.
#Outbound Evidence
#GET /v1/compliance/calls/{call_id}/outbound-evidence
Returns the safe outbound-compliance evidence summary for a scoped call. The response includes calling-window, registry-wash, consent/exemption, retry, and CLI-disclosure summaries only. It never returns raw phone numbers, imported CSV content, transcript text, recordings, ciphertext, API keys, bearer tokens, or provider credentials.
#Response Safety
Responses return only safe summaries and campaign configuration fields:
- masked contact phone placeholders. The current contact list uses
**** - contact status and outcome metadata
- registry, consent, and exemption summaries
- campaign counts and validation findings
- linked-call aggregate counts
- configured campaign compliance fields, including CLI disclosure values
The API never returns raw imported CSV content, decrypted imported contact phone numbers, plaintext personalization, encrypted database fields, secret-backend material, raw runtime configuration, or provider credentials.
#Error Semantics
Outbound campaign domain errors use the partner error envelope from core.error_envelope, except framework-level authentication and validation errors which use the shared API error shape.
| Condition | Status |
|---|---|
| Missing, invalid, revoked, or expired authentication | 401 Unauthorized |
| Valid bearer token with no active supported assignment | 404 Not Found |
| Role not allowed, out-of-scope campaign, missing campaign, or invalid campaign id | 404 Not Found |
Missing or invalid Idempotency-Key on required mutation routes | 400 Bad Request |
| FastAPI request-shape validation failure | 422 Unprocessable Entity |
| Campaign, CSV, registry, runtime-readiness, or state validation failure | 422 Unprocessable Entity |
Duplicate Idempotency-Key with a different request body | 422 Unprocessable Entity |
| Registry idempotency replay cannot find a completed prior record | 409 Conflict |
Missing (client_id, data_env) route in the tenant routing map | 500 Internal Server Error |
#Deletion Policy
There is no delete endpoint. Use POST /v1/outbound-campaigns/{campaign_id}/cancel to stop a campaign while preserving audit and reporting history.