---
title: "Create Campaign"
method: POST
path: "/v2/campaigns"
tags: ["Campaigns V2"]
---

# Create Campaign

`POST /v2/campaigns`

Creates a new campaign. `placements` and `audiences` are both optional — omit them (or send empty arrays) to create a campaign with just a name, as in the legacy dashboard, and configure it later; when no audiences are supplied the server creates a single default control audience (a 0% holdout). Audience targeting is set with a structured `rule_conditions` rule, which is compiled into the audience's expression and drives `expression_cel` for modern SDKs; omit it to match all users. The legacy raw `expression` field is no longer accepted. Requires campaigns:write scope.

## Request body

- object
  - `application_id` string, required — ID of the application to create the campaign in
  - `description` string, required — Description of the campaign
  - `notes` string, nullable — Internal notes about the campaign
  - `placements` object[] — List of placements (triggers) for the campaign. Optional — omit it or send an empty array to create a campaign with just a name (as in the legacy dashboard) and add placements later in the editor
    - `event_name` string, required — Event name that triggers this placement
    - `enabled` boolean — Whether this placement is enabled. Defaults to true
    - `remove_from_other_campaigns` boolean — If true and this event name exists in another campaign, move it to this campaign. If false and event exists elsewhere, returns a conflict error
  - `audiences` object[] — List of audiences for the campaign. Optional — when omitted or empty the server creates a single default control audience (one 0% holdout targeting unsubscribed users), matching the legacy name-only create flow
    - `enabled` boolean — Whether this audience is enabled. Defaults to true
    - `rule_conditions` RuleConditions — Structured audience targeting rule (the dashboard rule-editor AST). Compiled server-side into the runtime expression and `expression_cel`. Provide an empty `conditions` array to match all users.
      - `operator` 'and' | 'or', required
      - `conditions` RuleCondition[], required
        - union
          - object
            - `type` 'condition', required
            - `lhs` union, required
              - …
            - `operator` 'eq' | 'neq' | 'c' | 'nc' | 'gt' | 'gte' | 'lt' | 'lte' | 't' | 'f' | 'null' | 'nnull', required
            - `rhs` union, required
              - …
          - object
            - `type` 'group', required
            - `operator` 'and' | 'or', required
            - `conditions` RuleCondition[], required
      - `entitlements` object
        - `type` 'entitlement', required
        - `rule` union, required
          - object
            - `type` 'no_active_entitlements', required
          - object
            - `type` 'always_true', required
          - object
            - `type` 'entitlement_conditions', required
            - `conditions` object[], required
              - …
          - object
            - `type` 'any_will_not_renew', required
          - object
            - `type` 'any_in_trial', required
          - object
            - `type` 'any_expired_no_active', required
          - object
            - `type` 'any_active_subs_auto_renew_disabled', required
          - object
            - `type` 'any_in_billing_retry', required
    - `description` string, nullable — Human-readable description of the audience
    - `variant_optimization` union — Optimization strategy for variant allocation. Defaults to `none`
      - 'none' — No optimization, use fixed percentages
      - 'ucb1_bandit' — UCB1 multi-armed bandit optimization
    - `variants` object[], required — List of variants in this audience. At least one is required
      - `type` union, required — Whether this is a treatment or holdout variant
        - 'treatment' — Treatment variant that shows a paywall
        - 'holdout' — Holdout variant that does not show a paywall
      - `paywall` string, nullable, required — a string to be decoded into a number
      - `percentage` number — Traffic percentage allocated to this variant (0-100). When omitted on createVariant, the server applies the legacy default — first treatment / first holdout gets 100%, subsequent variants get 0%.

## Response `200`

Success

- object
  - `id` string, required — Unique identifier for the campaign
  - `object` 'campaign', required — Object type, always `campaign`
  - `application_id` string, required — ID of the application this campaign belongs to
  - `description` string, required — Description of the campaign
  - `notes` string, nullable, required — Internal notes about the campaign
  - `is_legacy` boolean, required — Whether this is a legacy campaign
  - `is_prioritized` boolean, required — Whether this campaign is prioritized for config preloading. At most one campaign per application may be prioritized.
  - `placements` object[], required — List of placements (triggers) in this campaign
    - `id` string, required — Unique identifier for the placement
    - `object` 'placement', required — Object type, always `placement`
    - `event_name` string, required — Event name that triggers this placement
    - `enabled` boolean, required — Whether this placement is enabled
  - `audiences` object[], required — List of audiences in this campaign
    - `id` string, required — Unique identifier for the audience
    - `object` 'audience', required — Object type, always `audience`
    - `enabled` boolean, required — Whether this audience is enabled
    - `expression` string, nullable, required — Compiled filter expression for matching users, or null for all users
    - `rule_conditions` unknown, required
    - `expression_cel` string, nullable, required — The CEL expression evaluated by modern SDKs, derived from `rule_conditions`. Null when the audience has no structured rule.
    - `expression_sql` string, nullable, required — The dashboard-side SQL compilation of `rule_conditions` (for preview/copy). Null when the audience has no structured rule.
    - `rank` string, required — Priority rank of this audience within the campaign
    - `description` string, nullable, required — Human-readable description of the audience
    - `variant_optimization` union, required — Optimization strategy for variant allocation
      - 'none' — No optimization, use fixed percentages
      - 'ucb1_bandit' — UCB1 multi-armed bandit optimization
    - `variants` object[], required — List of variants in this audience
      - `id` string, required — Unique identifier for the variant
      - `object` 'variant', required — Object type, always `variant`
      - `type` union, required — Whether this is a treatment or holdout variant
        - 'treatment' — Treatment variant that shows a paywall
        - 'holdout' — Holdout variant that does not show a paywall
      - `paywall` string, nullable, required — a string to be decoded into a number
      - `percentage` number, required — Traffic percentage allocated to this variant (0-100)
      - `created_at` string, required — ISO 8601 timestamp of when the variant was created
    - `frequency_limit` object, nullable, required — How often each user can be matched into this audience
      - `occurrences` integer, required — an integer
      - `interval` union, required — Time window for a frequency limit
        - object
          - `type` 'infinity', required
        - object
          - `type` 'minutes', required
          - `minutes` integer, required — an integer
  - `created_at` string, required — ISO 8601 timestamp of when the campaign was created
  - `updated_at` string, required — ISO 8601 timestamp of when the campaign was last updated
  - `archived` boolean, required — Whether the campaign is archived

## Other responses

- `400` — The request did not match the expected schema
- `401` — No API key was provided in the request
- `403` — The API key does not have permission to perform this action
- `404` — The requested resource was not found
- `409` — A placement with this event name already exists in another campaign. Use remove_from_other_campaigns=true to move it.
- `429` — Too many requests have been made in a short period
- `500` — A required post-operation side effect failed

---

[API](https://skmtc.dev/superwall/apis/superwall-api-v2.md) · [All operations](https://skmtc.dev/superwall/apis/superwall-api-v2/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/superwall/superwall-api-v2/revisions/44900ad77d2c/schema)
