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

# Campaigns API

> Create and manage batch campaigns with schedules, frequency caps, and file output configuration. Trigger and monitor individual campaign runs.

<Note>
  **See also**: [Runs concept and configuration](/operations-reporting/runs) for what this API powers, when to call it, and how it is configured.
</Note>

Campaigns define recurring batch execution configurations. Each campaign targets a decision flow and customer segment, with schedule, volume, and file settings. Campaign runs are individual execution instances.

<Info>
  See the [Campaigns feature page](/operations-reporting/runs) for UI guidance on creating and managing campaigns.
</Info>

## Base path

```
/api/v1/runs
```

***

## List campaigns

```
GET /api/v1/runs
```

Returns a paginated list of campaigns with latest run info.

### Query parameters

| Parameter | Required | Type    | Description                                    |
| --------- | -------- | ------- | ---------------------------------------------- |
| `limit`   | No       | integer | Max results per page. Default `50`, max `200`. |
| `offset`  | No       | integer | Records to skip. Default `0`.                  |

### Response `200`

```json theme={null}
{
  "runs": [
    {
      "id": "camp_001",
      "name": "Weekly High-Value Campaign",
      "status": "active",
      "scheduleType": "weekly",
      "scheduleDayOfWeek": 1,
      "scheduleTime": "09:00",
      "scheduleTimezone": "America/New_York",
      "decisionFlowName": "Q1 Cross-Sell",
      "segmentName": "High Value Customers",
      "segmentCustomerCount": 4521,
      "runCount": 3,
      "lastRunAt": "2026-03-15T14:00:00.000Z",
      "latestRun": {
        "id": "run_003",
        "status": "completed",
        "runNumber": 3
      }
    }
  ],
  "total": 5
}
```

***

## Create campaign

```
POST /api/v1/runs
```

Creates a new campaign configuration. Does **not** trigger execution -- use the trigger endpoint to start a run.

### Request body

| Field                | Required | Type      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| -------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `decisionFlowId`     | **Yes**  | string    | Decision flow to execute                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `segmentId`          | **Yes**  | string    | Customer segment to target                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `name`               | No       | string    | Campaign name (auto-generated if omitted)                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `description`        | No       | string    | Campaign purpose and notes                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `status`             | No       | enum      | `draft`, `active`, `paused`, `archived`. Default: `draft`                                                                                                                                                                                                                                                                                                                                                                                                          |
| `scheduleType`       | No       | enum      | `manual`, `daily`, `weekly`, `monthly`, `custom`. Default: `manual`                                                                                                                                                                                                                                                                                                                                                                                                |
| `scheduleDayOfWeek`  | No       | integer   | 0=Sun..6=Sat (for weekly)                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `scheduleDayOfMonth` | No       | string    | 1-28 or `last_working_day` (for monthly)                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `scheduleTime`       | No       | string    | HH:mm format. Default: `09:00`                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `scheduleTimezone`   | No       | string    | IANA timezone identifier (for example `America/New_York`). Default: UTC.                                                                                                                                                                                                                                                                                                                                                                                           |
| `scheduleCron`       | No       | string    | Cron expression, used only when `scheduleType` is `custom` (e.g., `0 9 * * 1`).                                                                                                                                                                                                                                                                                                                                                                                    |
| `frequencyCaps`      | No       | object    | `{ maxTotalPerRun?, maxPerOffer?, maxPerChannel? }` — accepted and stored, but **not currently enforced** by the batch executor. Use a [Contact Policy](/decisioning/contact-policies) `frequency_cap` rule instead for actual capping.                                                                                                                                                                                                                            |
| `fileConfig`         | No       | object    | `Run.fileConfig` v2 — the campaign's outbound-file config that the batch executor reads for every file-mode channel in the run: `{ destinationConnectorId?, destinationPath?, format?, delimiter?, includeHeader?, namingPattern?, columns?: {name, source, transforms?}[] }`. Validated permissively (`validateRunFileConfig`); an omitted/empty/legacy value is accepted. See [File Output Configuration](/operations-reporting/runs#file-output-configuration). |
| `channelIds`         | No       | string\[] | Batch channel IDs to target (empty = all batch channels)                                                                                                                                                                                                                                                                                                                                                                                                           |

<Info>
  When `status` is `active` and `scheduleType` is not `manual`, the campaign is picked up by the `/api/v1/cron/campaign-scheduler` route (5-minute cadence in the in-process maintenance scheduler) and a campaign run is triggered automatically once the schedule is due — see [Schedule Configuration](/operations-reporting/runs#schedule-configuration) for the full firing semantics.
</Info>

### Example request

```json theme={null}
{
  "decisionFlowId": "df_001",
  "segmentId": "seg_001",
  "name": "Weekly High-Value Campaign",
  "status": "active",
  "scheduleType": "weekly",
  "scheduleDayOfWeek": 1,
  "scheduleTime": "09:00",
  "scheduleTimezone": "America/New_York",
  "channelIds": ["ch_batch_email"],
  "fileConfig": {
    "destinationConnectorId": "conn_s3_outbound",
    "destinationPath": "campaigns/{{date}}/",
    "format": "csv",
    "columns": [
      { "name": "email", "source": "contact.email", "transforms": [{ "op": "lowercase" }] },
      { "name": "offer", "source": "offer.name" },
      { "name": "tier", "source": "attribute.loyalty_tier", "transforms": [{ "op": "uppercase" }] }
    ]
  }
}
```

The `fileConfig` above is the campaign-owned file output (`Run.fileConfig` v2) — it drives the file every file-mode channel in the campaign writes. See [File Output Configuration](/operations-reporting/runs#file-output-configuration) for the full field and column-source reference.

### Response `201`

Returns the created campaign object.

***

## Update campaign

```
PUT /api/v1/runs/:id
```

Updates campaign configuration. Accepts any combination of the following fields: `name`, `description`, `status`, `scheduleType`, `scheduleDayOfWeek`, `scheduleDayOfMonth`, `scheduleTime`, `scheduleTimezone`, `scheduleCron`, `frequencyCaps`, `fileConfig`, `channelIds`, `decisionFlowId`, `segmentId`.

<Note>
  Unlike most update endpoints, PUT allows changing `decisionFlowId` and `segmentId` to retarget a campaign to a different decision flow or customer segment.
</Note>

***

## Delete campaign

```
DELETE /api/v1/runs/:id
```

Deletes the campaign and all its runs. Requires `admin` role.

***

## Get campaign detail

```
GET /api/v1/runs/:id
```

Returns the campaign config with the last 10 runs, resolved offer names in summaries, and enriched per-customer results.

***

## Trigger a campaign run

```
POST /api/v1/runs/:id/campaign-runs
```

Creates and starts a new campaign run. The run is enqueued for processing by the worker tier, with inline fallback when the queue is unavailable.

The run is reserved under a `FOR UPDATE` lock on the campaign, so the endpoint is **idempotent under concurrency**: two racing triggers (a double-click, a retry, or a manual trigger coinciding with a scheduled tick) can't each mint a run and each execute the full segment. A trigger issued while a run for the campaign is already `pending` or `running` returns that in-flight run with `200` (below) rather than starting a second.

### Response `201` (new run created)

```json theme={null}
{
  "id": "crun_001",
  "campaignId": "camp_001",
  "runNumber": 4,
  "triggeredBy": "manual",
  "status": "pending",
  "createdAt": "2026-03-22T09:00:00.000Z"
}
```

### Response `200` (a run is already in flight)

Returns the existing `pending`/`running` campaign run unchanged — no second execution is started.

```json theme={null}
{
  "id": "crun_000",
  "campaignId": "camp_001",
  "runNumber": 3,
  "triggeredBy": "manual",
  "status": "running",
  "createdAt": "2026-03-22T08:55:00.000Z"
}
```

***

## List campaign runs

```
GET /api/v1/runs/:id/campaign-runs
```

Returns execution history for a campaign, ordered by run number descending (max 50).

<Note>
  Each run's `summary.fileOutputs[].filePath` is a retrievable download — `GET /api/v1/runs/artifacts/:id` when it wasn't uploaded to S3. See [Retrieving Generated Files](/operations-reporting/runs#retrieving-generated-files).
</Note>

***

## Execute a run inline (background)

```
POST /api/v1/runs/execute-inline
```

Fire a run execution in the background and return immediately. The endpoint dispatches the batch executor without awaiting it — the HTTP response is sent before the batch starts producing output, and any per-run failure is recorded in the run's status and logs rather than surfaced to the caller.

This endpoint is for inline triggers from a single-tenant dev environment or a backup path when the worker tier is degraded. Production triggering should use `POST /api/v1/runs/:id/campaign-runs` so the run gets a campaign-run record, retry semantics, and DLQ visibility.

### Request Body

<ParamField body="runId" type="string" required>
  Identifier of the run record that the executor will update with `status`, `startedAt`, and `completedAt`.
</ParamField>

<ParamField body="decisionFlowId" type="string" required>
  Decision flow to execute against.
</ParamField>

<ParamField body="segmentViewName" type="string" required>
  Customer-segment view name. The executor reads from this Postgres view to enumerate target customers.
</ParamField>

### Response

Status `200`.

```json theme={null}
{
  "status": "executing",
  "runId": "run_42"
}
```

The response is sent the moment the executor is dispatched. The caller monitors progress by polling `GET /api/v1/runs/:id`.

### Status codes

| Code      | When                                                                                                                                                                                                                                                                       |
| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 200       | Executor dispatched (returned immediately, before completion)                                                                                                                                                                                                              |
| 400       | Missing `runId`, `decisionFlowId`, or `segmentViewName`                                                                                                                                                                                                                    |
| 401 / 403 | Caller is not authenticated, or not `admin` / `editor`                                                                                                                                                                                                                     |
| 404       | `runId`, `decisionFlowId`, or `segmentViewName` does not resolve to a Run / Decision Flow / Segment **owned by the caller's tenant** — all three are validated before dispatch, so an unknown or foreign id fails fast instead of starting a run that fails asynchronously |

### Roles

admin, editor.

<Note>
  Errors raised by the batch executor after the response is sent are logged but never surfaced to the caller. The run record's `status` and `error` fields are the source of truth for outcome — poll `GET /api/v1/runs/:id` to see the final state.
</Note>

***

## Campaign statuses

| Status     | Description                                  |
| ---------- | -------------------------------------------- |
| `draft`    | Created but not yet active                   |
| `active`   | Live -- scheduled runs execute automatically |
| `paused`   | Temporarily disabled                         |
| `archived` | Retired, preserved for history               |

## Run statuses

| Status                  | Description                                                                                                                                                                                      |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pending`               | Waiting for worker to pick up                                                                                                                                                                    |
| `running`               | Processing customers                                                                                                                                                                             |
| `completed`             | All customers processed                                                                                                                                                                          |
| `completed_with_errors` | Finished but something went wrong for a subset of the run — a delivery failure or a per-customer decisioning error. See `error` and `results.summary.errorCount` on the run/campaign-run record. |
| `failed`                | Unrecoverable error                                                                                                                                                                              |

***

## Role requirements

| Method                | Minimum role |
| --------------------- | ------------ |
| GET (list/detail)     | `viewer`     |
| POST (create/trigger) | `editor`     |
| PUT (update)          | `editor`     |
| DELETE                | `admin`      |
