# Create a campaign (/docs/api-reference/whatsapp/createWhatsappCampaign)

> Create a campaign as a draft. It sends nothing until you launch it. Add contacts and launch it in the next calls. Only an approved template can be used, so every campaign starts with one. Scheduling is not offered.

> **Set up WhatsApp first.** Connect a WhatsApp number in the dashboard or with [Connect numbers](/docs/api-reference/whatsapp/importWhatsappNumbers). Included on every paid plan. [See plans](https://omnidim.io/pricing).

**POST** `/whatsapp/campaigns`

Create a campaign as a draft. It sends nothing until you launch it. Add
contacts and launch it in the next calls. Only an approved template can
be used, so every campaign starts with one. Scheduling is not offered.

```yaml
operationId: createWhatsappCampaign
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        required:
          - name
          - phone_number_id
          - template_id
        properties:
          name:
            type: string
            maxLength: 255
          phone_number_id:
            type: integer
          template_id:
            type: integer
            description: An approved template on that number.
          campaign_mode:
            type: string
            enum:
              - broadcast
              - broadcast_reply
            default: broadcast
            description: '`broadcast_reply` lets the agent answer replies. It needs an agent.'
          agent_id:
            type: integer
            description: The agent that answers replies.
          data_source:
            type: string
            enum:
              - csv
              - webhook
            default: csv
          is_dynamic:
            type: boolean
            default: false
          variable_mapping:
            type: object
            description: Template field to contact field or static text.
            additionalProperties: true
      example:
        name: October promo
        phone_number_id: 412
        template_id: 3676
        campaign_mode: broadcast
responses:
  '201':
    description: The draft campaign.
    content:
      application/json:
        schema:
          type: object
          properties:
            id:
              type: integer
            name:
              type: string
            status:
              type: string
              enum:
                - draft
                - scheduled
                - in_progress
                - paused
                - completed
                - failed
                - cancelled
            pause_reason:
              type: string
              nullable: true
              enum:
                - human
                - balance
                - channel_down
                - monthly_limit
              description: Why it paused, or null. Only `human` waits for you; the rest resume on their own.
            campaign_mode:
              type: string
              enum:
                - broadcast
                - broadcast_reply
            send_mode:
              type: string
              enum:
                - review
                - on_arrival
            data_source:
              type: string
              enum:
                - csv
                - webhook
            is_dynamic:
              type: boolean
            phone_number_id:
              type: integer
            template:
              type: object
              nullable: true
              properties:
                id:
                  type: integer
                name:
                  type: string
            agent_id:
              type: integer
              nullable: true
              description: The agent that answers replies, when one is attached.
            variable_mapping:
              type: object
              description: Template field to contact field or static text.
              additionalProperties: true
            counts:
              type: object
              properties:
                total:
                  type: integer
                sent:
                  type: integer
                delivered:
                  type: integer
                read:
                  type: integer
                replied:
                  type: integer
                failed:
                  type: integer
                skipped:
                  type: integer
                opted_out:
                  type: integer
            receipts_reported:
              type: boolean
              description: This number reports delivery and read receipts, so the rates are real.
            delivery_rate:
              type: number
              nullable: true
              description: Delivered as a percentage of sent. Null when receipts are not reported.
            read_rate:
              type: number
              nullable: true
              description: Read as a percentage of delivered. Null when receipts are not reported.
            reply_rate:
              type: number
              description: Replied as a percentage of sent.
            spend:
              type: object
              nullable: true
              description: What the campaign cost so far. Null without billing access.
              properties:
                amount:
                  type: number
                currency:
                  type: string
                  example: USD
            created_at:
              type: string
              nullable: true
  '400':
    description: |
      `invalid_parameter`, `invalid_json`, `not_supported` (a scheduling
      field), or `invalid_request` (a `broadcast_reply` campaign with no
      agent).
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
  '401':
    description: Missing or invalid API key.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: unauthorized
          error_description: Missing or invalid API key
          code: invalid_api_key
          request_id: req_6e2b9d4f1a38
  '403':
    description: |
      `feature_not_enabled` when your plan or role cannot use WhatsApp,
      `permission_denied` when the key's user cannot make changes, or
      `service_unavailable` when the account is on hold.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: forbidden
          error_description: You do not have permission to make changes to WhatsApp.
          code: permission_denied
          request_id: req_9f2c1a4b7d3e
  '404':
    description: '`number_not_found`, `template_not_found` or `agent_not_found`.'
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
  '500':
    description: Something went wrong on our side. Quote `ref` to support.
    content:
      application/json:
        schema:
          type: object
          description: |
            The error envelope for every WhatsApp route. Some errors add keys:
            `fields` (the template parameters a send still needs), `ref` (a
            500's support reference) and `failed` (on `import_failed`).
          properties:
            error:
              type: string
              description: Machine-readable category, such as `invalid_request`.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: Stable, specific reason, such as `window_closed`.
            request_id:
              type: string
              description: >-
                Correlates this response with the server log. Also sent as the `X-Request-Id`
                header.
            fields:
              type: array
              items:
                type: string
              description: >-
                On a template send missing parameters (`missing_fields`), the field keys you still
                need.
            ref:
              type: string
              description: On a `500`, the reference to quote to support.
            failed:
              type: array
              items:
                type: object
                properties:
                  phone_number_id:
                    type: string
                  message:
                    type: string
                    example: >-
                      This WhatsApp number is already connected to another account. Disconnect it
                      there first, then import it again. Contact support if you cannot access that
                      account.
              description: On `import_failed`, each number and why it could not be connected.
        example:
          error: server_error
          error_description: >-
            Something went wrong on our side. Please try again shortly, or contact support with
            reference 9a1f3e8b02c4.
          ref: 9a1f3e8b02c4
          code: server_error
          request_id: req_9a1f3e8b02c4
```

**curl**

```bash
curl -X POST "https://backend.omnidim.io/api/v1/whatsapp/campaigns" \
  -H "Authorization: Bearer $OMNIDIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "October promo",
  "phone_number_id": 412,
  "template_id": 3676,
  "campaign_mode": "broadcast"
}'
```