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

# Workflow Campaigns

> Run a published workflow over thousands of contacts: create a campaign, upload entries as CSV or JSON, start it, and track progress with per-node reports.

## What is a Workflow Campaign?

A campaign runs one published workflow version over a batch of contacts. Where the [run endpoint](/docs/api-reference/workflows/run) handles one contact per request, a campaign takes uploads of up to 150,000 rows and turns each accepted row into an execution when started.

Two things are fixed at creation time:

* **The version pin.** Passing `"version": null` pins the latest published version *at that moment*; publishing newer versions later never changes a created campaign.
* **The entry schema.** Uploads are validated against the pinned version's start-node fields.

The lifecycle is `draft` → `scheduled` → `running` → (`paused` ↔ `running`) → `completed` or `aborted`.

***

## Running a campaign

<Steps>
  <Step title="Create the campaign">
    Use the [Create Campaign API](/docs/api-reference/workflow-campaigns/create) with the workflow to run:

    <CodeGroup>
      ```bash request theme={"system"}
      curl --location 'https://api.bolna.ai/workflow-campaigns' \
      --header 'Authorization: Bearer <api_key>' \
      --header 'Content-Type: application/json' \
      --data '{
        "workflow_id": "1f2a3b4c-5d6e-7f80-91a2-b3c4d5e6f708",
        "version": null,
        "name": "August loan follow-ups"
      }'
      ```

      ```json response theme={"system"}
      {
        "id": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
        "name": "August loan follow-ups",
        "workflow_id": "1f2a3b4c-5d6e-7f80-91a2-b3c4d5e6f708",
        "workflow_version": 1,
        "kind": "batch",
        "status": "draft",
        "entries_count": 0,
        "success_count": 0,
        "failure_count": 0,
        "created_at": "2026-08-22T11:00:00Z",
        "updated_at": "2026-08-22T11:00:00Z"
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Fetch the CSV template">
    The [template endpoint](/docs/api-reference/workflow-campaigns/entries-template) returns the exact header row this campaign expects — the pinned version's declared fields in author order:

    <CodeGroup>
      ```bash request theme={"system"}
      curl --location 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f/entries/template' \
      --header 'Authorization: Bearer <api_key>'
      ```

      ```csv response theme={"system"}
      mobile_number,loan_amount,city
      ```
    </CodeGroup>
  </Step>

  <Step title="Upload entries">
    Upload contacts with the [Upload Entries API](/docs/api-reference/workflow-campaigns/upload-entries) — as CSV or as a JSON array; the `Content-Type` header decides:

    <Tabs>
      <Tab title="CSV">
        <CodeGroup>
          ```bash request theme={"system"}
          curl --location 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f/entries' \
          --header 'Authorization: Bearer <api_key>' \
          --header 'Content-Type: text/csv' \
          --data-binary $'mobile_number,loan_amount,city\n+919876543210,60000,Pune\n+919876543211,not-a-number,Mumbai\n'
          ```

          ```json response theme={"system"}
          {
            "accepted": 1,
            "corrected": 0,
            "failed": 1,
            "entries_count": 1,
            "failures": [
              { "row": 3, "reference_id": null, "code": "invalid_field_type",
                "message": "loan_amount: expected number" }
            ],
            "warnings": []
          }
          ```
        </CodeGroup>
      </Tab>

      <Tab title="JSON">
        <CodeGroup>
          ```bash request theme={"system"}
          curl --location 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f/entries' \
          --header 'Authorization: Bearer <api_key>' \
          --header 'Content-Type: application/json' \
          --data '[
            { "reference_id": "cand_101", "mobile_number": "+919876543210", "loan_amount": 60000, "city": "Pune" },
            { "reference_id": "cand_102", "mobile_number": "+919876543211", "loan_amount": 45000, "city": "Mumbai" }
          ]'
          ```

          ```json response theme={"system"}
          {
            "accepted": 2,
            "corrected": 0,
            "failed": 0,
            "entries_count": 3,
            "failures": [],
            "warnings": []
          }
          ```
        </CodeGroup>
      </Tab>
    </Tabs>

    Validation runs in two stages. A whole-upload shape check first — an undeclared column rejects everything with `422 unknown_field`, a missing required column with `missing_required_field`, and nothing is persisted. Then rows are validated individually: failures are reported per row (`invalid_phone`, `invalid_field_type`, `missing_required_value`, `duplicate_reference_id`, ...) while good rows are accepted. For CSV, `row` counts the header as line 1, so the first data row is `2`.

    <Tip>
      Fix-and-reupload just works: a row whose earlier version failed validation is replaced in place and counted in `corrected`. `reference_id` is each contact's identity — omit it and one is derived from the row's content, so identical rows deduplicate automatically.
    </Tip>
  </Step>

  <Step title="Start the campaign">
    <CodeGroup>
      ```bash request theme={"system"}
      curl --location --request POST 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f:start' \
      --header 'Authorization: Bearer <api_key>' \
      --header 'Content-Type: application/json' \
      --data '{}'
      ```

      ```json response theme={"system"}
      {
        "status": "scheduled"
      }
      ```
    </CodeGroup>

    <Warning>
      If the pinned definition contains an agent node, starting the campaign places real calls to every pending entry.
    </Warning>

    While it runs you can [pause](/docs/api-reference/workflow-campaigns/pause) (nothing new starts; timer waits hold), [resume](/docs/api-reference/workflow-campaigns/resume), or [abort](/docs/api-reference/workflow-campaigns/abort) (terminal — every execution stops before its next external action). A single contact can be stopped with the [Cancel Execution API](/docs/api-reference/workflow-executions/cancel).
  </Step>

  <Step title="Monitor progress">
    List executions as they progress, filtered by status or outcome:

    <CodeGroup>
      ```bash request theme={"system"}
      curl --location 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f/executions?status=running&limit=50' \
      --header 'Authorization: Bearer <api_key>'
      ```

      ```json response theme={"system"}
      {
        "items": [
          {
            "id": "exec:7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f:cand_101",
            "campaign_id": "7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
            "status": "running",
            "current_node_id": "n_retry",
            "termination_reason": null,
            "outcome": null,
            "call_count": 1,
            "created_at": "2026-08-22T11:10:00Z",
            "updated_at": "2026-08-22T11:12:04Z"
          }
        ],
        "total": 1,
        "limit": 50,
        "offset": 0
      }
      ```
    </CodeGroup>

    And pull the aggregated [report](/docs/api-reference/workflow-campaigns/report) — the `termination_reasons` keys are your end nodes' labels, and `funnel` counts attempts per node of your definition:

    <CodeGroup>
      ```bash request theme={"system"}
      curl --location 'https://api.bolna.ai/workflow-campaigns/7c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f/report' \
      --header 'Authorization: Bearer <api_key>'
      ```

      ```json response theme={"system"}
      {
        "executions": { "completed": 240, "running": 12 },
        "termination_reasons": { "reached": 180, "unreachable": 60 },
        "funnel": [
          { "node_id": "n_call", "status": "completed", "count": 252 },
          { "node_id": "n_retry", "status": "completed", "count": 84 },
          { "node_id": "n_end_reached", "status": "completed", "count": 180 }
        ]
      }
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Workflow Campaign APIs" icon="code" href="/docs/api-reference/workflow-campaigns/overview">
    Every campaign endpoint in detail
  </Card>

  <Card title="Workflow Execution APIs" icon="route" href="/docs/api-reference/workflow-executions/overview">
    Follow one contact node by node
  </Card>

  <Card title="Quickstart" icon="rocket" href="/docs/guides/workflows/quickstart">
    Build and publish the workflow a campaign runs
  </Card>

  <Card title="Batch Calling" icon="file-spreadsheet" href="/docs/guides/outbound/batch-calling">
    Single-call batches, when you don't need a graph
  </Card>
</CardGroup>
