# POST /v1/projects/:projectId/ads/:platform/campaigns (/docs/api/reference/ads/create-campaign)



<Endpoint method="POST" path="/v1/projects/:projectId/ads/{platform}/campaigns" scope="ads:write:campaigns" phase="1">
  Headline create-campaign endpoint per LOCK 6 — minimal required surface so the customer's "Create Campaigns on `{platform}`" button doesn't require ads expertise.
</Endpoint>

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 — see the [LOCK 10 investigation](/docs/internal/apple-campaign-create-investigation).

## Path parameters [#path-parameters]

<Parameters
  rows="[
  { name: 'projectId', type: 'string (uuid)', required: true, description: 'Project the new campaign belongs to.' },
  { name: 'platform', type: 'string', required: true, description: 'Platform.', enum: ['meta', 'tiktok', 'apple'] },
]"
/>

## Body [#body]

<Parameters
  title="Body"
  rows="[
  { name: 'adAccountId', type: 'string (uuid)', required: true, description: 'Layers ad-account id from [`GET …/ad-accounts`](/docs/api/reference/ads/list-ad-accounts).' },
  { name: 'name', type: 'string', required: true, description: '1-128 chars. Shows up in the platform\'s ad manager.' },
  { name: 'outcome', type: 'string', required: true, description: 'Platform-normalized outcome.', enum: ['conversions', 'traffic', 'app_installs', 'engagement', 'lead_generation'] },
  { name: 'dailyBudgetCents', type: 'integer', description: 'Daily budget in cents. Required if `lifetimeBudgetCents` is not set.' },
  { name: 'lifetimeBudgetCents', type: 'integer', description: 'Lifetime budget in cents. Required if `dailyBudgetCents` is not set.' },
  { name: 'campaignManagementMode', type: 'string', required: true, description: 'Sets the new campaign\'s tactical + structural authority.', enum: ['manual', 'draft', 'autopilot'] },
  { name: 'creativeMode', type: 'string', required: true, description: 'Sets the new campaign\'s creative authority.', enum: ['manual', 'draft', 'autopilot'] },
  { name: 'startTime', type: 'string (ISO 8601, UTC Z)', description: 'Optional start time. Defaults to now.' },
  { name: 'endTime', type: 'string (ISO 8601, UTC Z)', description: 'Optional end time. Required for some platform/outcome combos (TikTok lifetime-budget).' },
  { name: 'bidStrategy', type: 'object', description: 'Optional override of the platform default. See platform-specific notes.' },
  { name: 'targeting', type: 'object', description: 'Optional override of the platform default targeting.' },
  { name: 'metadata', type: 'object', description: 'Free-form, ≤ 8KB. Round-tripped on read.' },
]"
/>

Mode → axis mapping:

| Mode        | `tactical` | `structural` | `creative` |
| ----------- | ---------- | ------------ | ---------- |
| `manual`    | `off`      | `off`        | `off`      |
| `draft`     | `draft`    | `draft`      | `draft`    |
| `autopilot` | `auto`     | `auto`       | `auto`     |

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

## Request [#request]

```bash
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"
  }'
```

`5000` cents = $50.00 daily.

## Response [#response]

<Response status="202" description="Create-campaign workflow accepted.">
  ```json
  {
    "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.
</Response>

<Response status="403" description="Authority axis denies the create.">
  ```json
  {
    "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"
      }
    }
  }
  ```
</Response>

## Errors [#errors]

| Code                   | When                                                                                            |
| ---------------------- | ----------------------------------------------------------------------------------------------- |
| `VALIDATION`           | Body shape error; both daily and lifetime budget set; missing required field.                   |
| `AUTHORITY_DENIED`     | Bucket-mode axis denies the create — see [ads write model](/docs/api/concepts/ads-write-model). |
| `FORBIDDEN_SCOPE`      | Key lacks `ads:write:campaigns`.                                                                |
| `NOT_FOUND`            | Project / ad account not in this org, or platform OAuth not connected.                          |
| `IDEMPOTENCY_CONFLICT` | Same Idempotency-Key replayed with a different body.                                            |

## See also [#see-also]

* [Run ads as partner](/docs/api/guides/run-ads-as-partner) — end-to-end guide
* [Ads write model](/docs/api/concepts/ads-write-model) — bucket-mode authority + simplified-create surface contract
* [`PATCH …/campaigns/:id/budget`](/docs/api/reference/ads/patch-campaign-budget)
* [`PATCH …/campaigns/:id/authority`](/docs/api/reference/ads/patch-campaign-authority)
* [Apple campaign-create investigation](/docs/internal/apple-campaign-create-investigation)
