apps/api/app/routers/campaign.py, prefix /api/v1/campaign. See Campaigns.
max_concurrency, schedule_config, and circuit_breaker are accepted as top-level fields on the request but are not top-level fields on the stored document. The router nests all three inside orchestrator_metadata on both create and update, and CampaignResponse returns them there. Read orchestrator_metadata.max_concurrency, not max_concurrency.
The file must end in .csv, must be non-empty, must be under CAMPAIGN_MAX_CSV_BYTES, and must validate — which means it must have a phone_number header column. Returns CampaignCsvUploadResponse:
contact_rows counts data rows with a non-blank phone_number. Pass source_id straight to create. Failure codes: 400 for the wrong extension, an empty file, or a validation error; 413 for oversize; 502 when MinIO is unreachable.
retry_config, schedule_config, and circuit_breaker are all optional. Omitting retry_config applies DEFAULT_CAMPAIGN_RETRY_CONFIG from apps/api/app/constants/campaign.py; omitting the other two leaves them absent from orchestrator_metadata entirely.Returns CampaignResponse: campaign_id, org_id, name, agent_id, source_type, source_id, state, total_rows, processed_rows, failed_rows, rate_limit_per_second, retry_config, orchestrator_metadata, from_number, created_by, and the five timestamps.Failure codes: 404 when the agent does not exist, 422 when it is not a telephony agent or has no number, 400 when max_concurrency exceeds the organisation ceiling or the source fails validation.
Bearer, and the caller must hold admin or super_admin in the active organisation — a member gets 403. This is the only role-gated route on this page; every other campaign route is member-accessible. 404 outside your organisation.Deletes the queued runs first, then the campaign document. This is a hard delete, not a state change — there is no deleted or archived value in CampaignState, and the campaign will not appear in GET /campaign/ afterwards. Call logs already written for calls the campaign placed are not removed.Returns SuccessResponse:
Bearer. All fields optional: name, rate_limit_per_second, max_concurrency, retry_config, schedule_config, circuit_breaker. The three nested-config fields are merged into the existing orchestrator_metadata rather than replacing it. A new max_concurrency is re-validated against the organisation ceiling. Returns the updated CampaignResponse.
Bearer. 201. Body { "name": "March outreach — redial" }. Creates a new campaign over the parent’s failed contacts, inheriting the agent, source, rate limit, retry config, and caller ID, and recording parent_campaign_id in orchestrator_metadata. Returns the child CampaignResponse. No failed contacts returns 400 No failed contacts to redial.
Bearer. Streams text/csv as an attachment named campaign_{campaign_id}.csv, with columns call_id, to_number, status, call_response, duration, created_at. Capped at 500 rows.