Docs
APIAPI referenceAds

POST /v1/projects/:projectId/ads/:platform/campaigns

Simplified create-campaign endpoint. Customer picks budget + outcome + management mode + creative mode; Layers handles the rest.

View as Markdown
POST/v1/projects/:projectId/ads/{platform}/campaigns
Phase 1stable
Auth
Bearer
Scope
ads:write:campaigns

Headline create-campaign endpoint per LOCK 6 — minimal required surface so the customer's "Create Campaigns on {platform}" button doesn't require ads expertise.

This is the simplified create-campaign surface. It accepts the four required config entities for the customer's intent (budget, outcome, management mode, creative mode) and defaults every tactical detail per platform best practice. Sophisticated partners can use the granular CRUD endpoints (per-budget, per-targeting, etc.) for explicit overrides.

Supported {platform}: meta, tiktok, apple. Apple campaign-create lifted in this release.

Path parameters

  • projectId
    string (uuid)required
    Project the new campaign belongs to.
  • platform
    stringrequired
    Platform.
    One of: meta, tiktok, apple

Body

Body
  • adAccountId
    string (uuid)required
    Layers ad-account id from [`GET …/ad-accounts`](/docs/api/reference/ads/list-ad-accounts).
  • name
    stringrequired
    1-128 chars. Shows up in the platform's ad manager.
  • outcome
    stringrequired
    Platform-normalized outcome.
    One of: conversions, traffic, app_installs, engagement, lead_generation
  • dailyBudgetCents
    integeroptional
    Daily budget in cents. Required if `lifetimeBudgetCents` is not set.
  • lifetimeBudgetCents
    integeroptional
    Lifetime budget in cents. Required if `dailyBudgetCents` is not set.
  • campaignManagementMode
    stringrequired
    Sets the new campaign's tactical + structural authority.
    One of: manual, draft, autopilot
  • creativeMode
    stringrequired
    Sets the new campaign's creative authority.
    One of: manual, draft, autopilot
  • adsContentIds
    string[] (adc_…)optional
    The creatives this campaign runs, in order — the ids GET …/ads-content returns. Optional on the wire, but REQUIRED for meta and tiktok, where omitting it returns 422 CREATIVE_SELECTION_REQUIRED: Layers never selects creatives on your behalf. Ignored for apple, whose keyword campaigns carry no creative. Naming more than the platform runs is 422 CREATIVE_SELECTION_TOO_LARGE — at most 10 creatives on meta and 3 on tiktok.
  • startTime
    string (ISO 8601, UTC Z)optional
    Optional start time. Defaults to now.
  • endTime
    string (ISO 8601, UTC Z)optional
    Optional end time. Required for some platform/outcome combos (TikTok lifetime-budget).
  • bidStrategy
    objectoptional
    Optional override of the platform default. See platform-specific notes.
  • targeting
    objectoptional
    Optional override of the platform default targeting.
  • metadata
    objectoptional
    Free-form, ≤ 8KB. Round-tripped on read.

Mode → axis mapping:

Modetacticalstructuralcreative
manualoffoffoff
draftdraftdraftdraft
autopilotautoautoauto

The two mode params let you mix-and-match. Example: campaignManagementMode: "draft" + creativeMode: "autopilot" produces {tactical: "draft", structural: "draft", creative: "auto"}.

Request

curl -X POST https://api.layers.com/v1/projects/$PROJECT_ID/ads/meta/campaigns \
  -H "Authorization: Bearer $LAYERS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "adAccountId": "aa_01HXF1...",
    "name": "Q3 prospecting",
    "outcome": "conversions",
    "dailyBudgetCents": 5000,
    "campaignManagementMode": "draft",
    "creativeMode": "autopilot",
    "adsContentIds": ["adc_01HXF2...", "adc_01HXF3..."]
  }'

5000 cents = $50.00 daily.

Response

202Create-campaign workflow accepted.
{
  "jobId": "job_01HZX9...",
  "campaignId": "cmp_01HZX9...",
  "verdictId": "ver_01HZX9...",
  "kind": "ads_create_campaign",
  "status": "running",
  "platform": "meta",
  "authority": {
    "creative": "auto",
    "tactical": "draft",
    "structural": "draft"
  },
  "locationUrl": "/v1/jobs/job_01HZX9..."
}

The Layers campaignId is allocated immediately. Poll the job for the platform-side mutation; the platform-side id appears on subsequent GET …/campaigns/:campaignId calls.

verdictId is the audit-row UUID — quote it to Layers support.

403Authority axis denies the create.
{
  "error": {
    "code": "AUTHORITY_DENIED",
    "message": "Layer's structural axis is set to 'off'; campaign-create denied.",
    "requestId": "req_...",
    "details": {
      "verdictId": "ver_01HZX9...",
      "axis": "structural",
      "currentMode": "off"
    }
  }
}

Errors

CodeWhen
VALIDATIONBody shape error; both daily and lifetime budget set; missing required field. For creatives, details.code narrows it: CREATIVE_SELECTION_REQUIRED (no adsContentIds on a meta/tiktok create) or CREATIVE_SELECTION_TOO_LARGE (more than the platform can launch — details.max and details.received carry the numbers). Both are refused before anything is dispatched.
AUTHORITY_DENIEDBucket-mode axis denies the create — see ads write model.
FORBIDDEN_SCOPEKey lacks ads:write:campaigns.
NOT_FOUNDProject / ad account not in this org, or platform OAuth not connected.
IDEMPOTENCY_CONFLICTSame Idempotency-Key replayed with a different body.

See also

On this page