> ## 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.

# Upload Campaign Entries API

> Bulk-upload contacts as CSV rows or a JSON array. Rows are validated individually, with per-row failures reported alongside the accepted count.

<Note>
  Validation happens in two stages. First a whole-upload shape check: every column must be a system field (`reference_id`, `mobile_number`, `name`, `email`) or a field the pinned version's start node declares — otherwise the entire upload is rejected with `422 unknown_field` or `missing_required_field` and nothing is persisted. Then each row is validated individually: bad rows land in `failures` with a code (`invalid_phone`, `invalid_field_type`, `missing_required_value`, ...) while good rows are accepted.
</Note>

<Info>
  Re-uploading a row whose earlier version failed validation replaces it in place — such rows are counted in both `accepted` and `corrected`. Fetch the expected header with the [CSV template endpoint](/docs/api-reference/workflow-campaigns/entries-template). Uploads are capped at 32 MB and 150,000 rows.
</Info>


## OpenAPI

````yaml POST /workflow-campaigns/{campaign_id}/entries
openapi: 3.1.0
info:
  title: Bolna API
  description: >-
    Use and leverage Bolna Voice AI using APIs through HTTP requests from any
    language in your applications and workflows.
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.bolna.ai
    description: Production server
security:
  - bearerAuth: []
paths:
  /workflow-campaigns/{campaign_id}/entries:
    post:
      description: >-
        Bulk-uploads contacts as CSV rows or a JSON array. Columns must be
        system fields (`reference_id`, `mobile_number`, `name`, `email`) or
        fields the pinned version's start node declares — an undeclared column
        rejects the whole upload with `422 unknown_field` before anything is
        persisted. Rows are then validated individually; per-row failures land
        in the response's `failures` array while valid rows are accepted.
      parameters:
        - in: path
          name: campaign_id
          required: true
          schema:
            type: string
            format: uuid
          description: The unique `id` of the campaign
      requestBody:
        content:
          text/csv:
            schema:
              type: string
              example: |
                mobile_number,name,city
                +919876543210,Asha,Pune
                +919876543211,Ravi,Mumbai
          application/json:
            schema:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowEntryInput'
        required: true
      responses:
        '200':
          description: Per-row upload result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowEntriesUploadResult'
        '404':
          description: Campaign not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '409':
          description: >-
            `invalid_state` — entries can only be added while the campaign is
            `draft`, `scheduled` or `running`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '413':
          description: Body exceeds 32 MB
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
        '422':
          description: >-
            `unknown_field` or `missing_required_field` (whole-upload shape
            check), or `validation_failed` for malformed CSV/JSON
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WorkflowError'
components:
  schemas:
    WorkflowEntryInput:
      type: object
      additionalProperties: true
      description: >-
        One contact row for a JSON entries upload. Same shape as the run request
        — declared fields may be flat or under `custom_fields`.
      properties:
        reference_id:
          type: string
          description: >-
            Stable identity for the contact. When omitted, an identity is
            derived from the row's content, so identical rows deduplicate.
        mobile_number:
          type: string
          example: '+919876543210'
        name:
          type: string
        email:
          type: string
        custom_fields:
          type: object
    WorkflowEntriesUploadResult:
      type: object
      required:
        - accepted
        - corrected
        - failed
        - entries_count
        - failures
      properties:
        accepted:
          type: integer
          description: Rows inserted, including corrections of previously failed rows.
        corrected:
          type: integer
          description: >-
            Subset of `accepted` that replaced a `failed_validation` row from an
            earlier upload.
        failed:
          type: integer
          description: Rows that failed validation.
        entries_count:
          type: integer
          description: The campaign's total entries after this upload.
        failures:
          type: array
          maxItems: 1000
          items:
            $ref: '#/components/schemas/WorkflowEntryFailure'
        warnings:
          type: array
          maxItems: 1000
          items:
            $ref: '#/components/schemas/WorkflowEntryFailure'
          description: >-
            Accepted rows with a non-blocking issue, e.g.
            `missing_default_recipient` on a phone-less row when the workflow
            dials `entry.mobile_number`.
    WorkflowError:
      type: object
      description: >-
        Error envelope returned by every workflow endpoint. `detail.code` is a
        stable machine-readable code; extra keys (such as `current_revision` on
        `revision_conflict`, `execution_id` on `duplicate_run`, or `issues` on
        `invalid_definition`) ride alongside it.
      required:
        - detail
      properties:
        detail:
          type: object
          additionalProperties: true
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable error code.
              example: revision_conflict
            message:
              type: string
              description: Human-readable explanation.
    WorkflowEntryFailure:
      type: object
      required:
        - row
        - code
        - message
      properties:
        row:
          type: integer
          description: >-
            1-based row position. For CSV the header counts as row 1, so the
            first data row is `2`.
        reference_id:
          type: string
          nullable: true
        code:
          type: string
          description: >-
            Row failure code, e.g. `invalid_phone`, `invalid_field_type`,
            `missing_required_value`, `duplicate_reference_id`, `empty_row`.
          example: invalid_field_type
        message:
          type: string
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer

````