---
title: "Create a standalone campaign"
method: POST
path: "/v1/ads/campaigns"
tags: ["Ad Campaigns"]
---

# Create a standalone campaign

`POST /v1/ads/campaigns`

Creates a campaign WITHOUT its first ad set / ad (the ODAX shell only). Ad sets join it
later via `existingCampaignId` on the create endpoints. A budget here is campaign-level
(CBO) by definition; omit it for ABO (each ad set carries its own budget). Created
`PAUSED` unless `status: ACTIVE`. The campaign materializes in `/v1/ads/tree` via the
next sync discovery pass.

**Idempotency:** send an `Idempotency-Key` header to make retries safe.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `accountId` string, required — Zernio SocialAccount id (posting or ads variant) used to resolve the Meta token.
  - `adAccountId` string, required — Meta ad account id (act_<n>).
  - `name` string, required
  - `goal` 'engagement' | 'traffic' | 'awareness' | 'video_views' | 'lead_generation' | 'lead_conversion' | 'job_applicants' | 'conversions' | 'app_promotion' | 'catalog_sales', required — Mapped to the ODAX objective (same mapping as POST /v1/ads/create).
  - `specialAdCategories` string[]
  - `budgetAmount` number — Campaign-level (CBO) budget in WHOLE currency units (USD: 50 = $50.00), NOT cents — Meta's own Marketing API takes this same number in minor units, so it is an easy and expensive mix-up. Requires budgetType.
  - `budgetType` 'daily' | 'lifetime'
  - `status` 'ACTIVE' | 'PAUSED'
  - `bidStrategy` 'LOWEST_COST_WITHOUT_CAP' | 'LOWEST_COST_WITH_BID_CAP' | 'COST_CAP' | 'LOWEST_COST_WITH_MIN_ROAS' — Campaign bid strategy. Meta stores `bid_strategy` alongside the budget, so this REQUIRES `budgetAmount` + `budgetType` on the same request; sending it without a campaign budget is a 400. A campaign carrying a strategy without its `bid_amount` makes every ad set created under it fail with an error that names the ad set (code 100, subcode 1815857), so the bad state is rejected up front rather than accepted. To bid at ad-set level, set the strategy there instead.
  - `bidAmount` number — Whole currency units (USD: 5 = $5.00). Required for LOWEST_COST_WITH_BID_CAP and COST_CAP; ignored otherwise.
  - `roasAverageFloor` number — Decimal ROAS multiplier (2.0 = 2.0x). Required for LOWEST_COST_WITH_MIN_ROAS.

## Response `201`

Campaign created

- object
  - `adAccountId` string
  - `campaignId` string — Platform id of the new campaign
  - `objective` string — Resolved ODAX objective (e.g. OUTCOME_SALES).
  - `status` 'ACTIVE' | 'PAUSED'

## Other responses

- `400` — Invalid input, or Meta rejected the create
- `401` — Unauthorized
- `501` — Only supported on Meta (facebook/instagram)

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/revisions/51932b099b2f/schema)
