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

# Create campaign

> Open a platform-managed campaign draft

`POST /api/v2/buyer/campaigns`

For `discovery` and `performance`, creates a campaign in `DRAFT` status for the
given advertiser. Flight dates and budget are required. Products, creatives,
and audiences attach afterward. The legacy `routingType` field is not a
campaign input; it is derived per media buy as temporary billing compatibility
metadata from `BillingParty`. It is never a storefront or
execution type and is independent of campaign mode, BYOA, and protocol
connectivity. Execute the campaign later to turn it into live media buys.

Buyer-owned labels are applied through V3 MCP `save_campaign`, not this REST
create. See [Dimensions and labels](/v2/object-guides/dimension).

## REST request

Every campaign budget is **GROSS**: `budget.total` is the all-in amount the buyer pays, with Apostra fee inside it. Every budget downstream is gross too — media buy and package budgets allocate against `budget.total` directly, and the media/fee split is derived per media buy at the fee terms locked when that buy is created (readable via its `budget_breakdown` — see [Budgets and fees](/v2/concepts/budgets-and-fees)). There is no fee-model input at creation — the campaign object has no `feeType` field.

## Request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/campaigns \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "advertiserId": 12345,
      "name": "Q2 2026 Tech Launch",
      "idempotencyKey": "campaign-q2-tech-launch-2026",
      "flightDates": { "startDate": "2026-05-15T00:00:00Z", "endDate": "2026-07-15T23:59:59Z" },
      "budget": { "total": 100000, "currency": "USD", "pacing": "EVEN" },
      "brief": "Premium video for tech-savvy professionals",
      "constraints": {
        "channels": ["ctv", "video"],
        "geo_countries": ["US", "CA"],
        "geo_metros": [{ "system": "nielsen_dma", "values": ["501", "803"] }],
        "language": ["en"]
      }
    }'
  ```

  ```json With pacing periods theme={null}
  {
    "advertiserId": 12345,
    "name": "Holiday Heavy-Up",
    "flightDates": { "startDate": "2026-11-01T00:00:00Z", "endDate": "2026-12-31T23:59:59Z" },
    "budget": { "total": 250000, "currency": "USD" },
    "pacingPeriods": {
      "mode": "weight",
      "periods": [
        { "label": "Pre-Black Friday", "start": "2026-11-01T00:00:00Z", "end": "2026-11-26T23:59:59Z", "weight": 1.0 },
        { "label": "Black Friday / Cyber Monday", "start": "2026-11-27T00:00:00Z", "end": "2026-12-02T23:59:59Z", "weight": 3.0 },
        { "label": "Holiday push", "start": "2026-12-03T00:00:00Z", "end": "2026-12-31T23:59:59Z", "weight": 1.5 }
      ]
    }
  }
  ```
</CodeGroup>

## Parameters

| Field | Type | Required | Notes |
| - | - | - | - |
| `advertiserId` | number | Yes | Owning advertiser |
| `name` | string | Yes | Campaign name (max 255) |
| `idempotencyKey` | string | No | Retry key for campaign creation (1-200 characters). Reusing it for the same advertiser returns the first campaign. |
| `flightDates` | object | Yes | `{ startDate, endDate }`, ISO 8601 dates |
| `budget` | object | Yes | `{ total, currency?, dailyCap?, pacing? }`. `currency` defaults to the advertiser's primary currency when omitted. `pacing` is `EVEN`, `ASAP`, or `FRONTLOADED`; every package inherits it unless its product line sets its own, and an unset pacing sends `even` (see [Which pacing a package gets](/v2/buyer/campaigns/packages#which-pacing-a-package-gets)) |
| `brief` | string | No | Free-text plan brief used to guide product selection |
| `constraints` | object | No | `channels` filter plus AdCP targeting overlay: `geo_countries`, `geo_metros`, `language`. Send only metro codes in `geo_metros`; use `resolve_targeting_dimension` when the buyer provides a name such as "LA DMA", and request `fields=geo_metro_names` on campaign reads when display labels are needed. Included labels return in `geo_metro_names`; excluded labels return in `geo_metro_names_exclude`. |
| `pacingPeriods` | object | No | Time-windowed pacing. `mode` is `weight` or `budget`; `periods[]` each carry `label`, `start`, `end`, and `weight` (weight mode) |

## Safe retries

Send an `idempotencyKey` when the client may retry after a timeout or lost
response. The key is scoped to the authenticated customer and remains bound to
the first campaign created for that advertiser. A replay returns that campaign
instead of creating another one, even if the retried body contains different
values. Generate a new key for a new campaign. Reusing a key for another
advertiser returns `409 CONFLICT`.

## Response

```json theme={null}
{
  "campaign": {
    "campaignId": "cmp_987654321",
    "advertiserId": "12345",
    "name": "Q2 2026 Tech Launch",
    "status": "DRAFT",
    "flightDates": { "startDate": "2026-05-15T00:00:00Z", "endDate": "2026-07-15T23:59:59Z" },
    "budget": { "total": 100000, "currency": "USD", "pacing": "EVEN" },
    "optimizationApplyMode": "MANUAL",
    "createdAt": "2026-05-01T09:00:00Z",
    "updatedAt": "2026-05-01T09:00:00Z"
  }
}
```

The campaign is created in `status: "DRAFT"`. `campaign.campaignId` is the stable identifier you pass to every sibling operation. Routing is decided per media buy at execution time — a campaign has no routing type of its own. When the request included a `discoveryId`, the response also carries `productGroups`, `budgetContext`, and `summary` from the discovery session.

## Errors

* `400 VALIDATION_ERROR` — missing required field, malformed `flightDates`, or non-positive `budget.total`.
* `409 CONFLICT` — `idempotencyKey` was already used for another advertiser.
* `404 NOT_FOUND` — `advertiserId` does not exist or is not visible to the authenticated account.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Campaign tasks" href="/v2/buyer/campaigns/tasks" icon="list-check">
    All campaign operations
  </Card>

  <Card title="Campaign overview" href="/v2/object-guides/campaign" icon="rocket">
    Fields, lifecycle, and concepts
  </Card>

  <Card title="Auto-select products" href="/v2/buyer/campaigns/tasks/auto-select-products" icon="wand-magic-sparkles">
    Populate a campaign from eligible storefront inventory
  </Card>

  <Card title="Execute campaign" href="/v2/buyer/campaigns/tasks/execute-campaign" icon="rocket">
    Launch into media buys
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.