---
title: "Get a Campaign Plan"
method: GET
path: "/context/campaign-plans/{slug}"
tags: ["Campaign Plans"]
---

# Get a Campaign Plan

`GET /context/campaign-plans/{slug}`

## Request

- Base URL: `https://api.adcrunch.dev`
- URL: `https://api.adcrunch.dev/context/campaign-plans/{slug}`
- Auth: HTTP bearer

Reads one Campaign Plan with its Line Items **in full**, and with the provider objects each line created.

Each line has four calculated fields: the provider that sells its channel, whether AdCrunch can execute the line, the window the line resolves to from its own dates and the Campaign Plan dates, and whether its amount is a total or a daily rate.

The plan has the three money figures. If `allocation.comparable` is `false`, the lines do not share one unit. Show "not comparable" in that condition. Do not show a total.

Send `revision` back as `base_revision` when you write to the Campaign Plan.

## Path parameters

- `slug` string, required

## Response `200`

The Campaign Plan with its Line Items **in full**. That is why there is no single-Line-Item fetch: progressive disclosure exists to keep unbounded prose out of a bundle, and a Line Item has none. This is the one answer that carries what each line created.

- object
  - `allocation` object, required: Derived on every read. Never stored.
    - `allocated` number, nullable, required: The sum of the Line Item budgets. `null` when the lines mix units, because that sum would be meaningless.
    - `comparable` boolean, required: False when the lines mix totals and daily rates. A surface must then print "not comparable" rather than a plausible-looking wrong total.
    - `unallocated` number, nullable, required: The envelope less the allocated, where both are known.
    - `unit` 'total' | 'daily', nullable, required: The shared unit, when the lines agree on one.
  - `brandId` string, nullable, required: Nullable and mutable: a plan may precede its brand, cover a portfolio, or be re-pointed. Deleting a Brand leaves its plans standing.
  - `createdAt` number, required: Milliseconds since the Unix epoch, UTC.
  - `createdBy` string, required
  - `currency` string, required: Editable until the first Line Item budget, and frozen thereafter.
  - `description` string, required
  - `endDate` string, nullable, required: A calendar date, `YYYY-MM-DD`.
  - `id` string, required
  - `lineItems` object[], required
    - `advertiserId` string, nullable, required
    - `ageMax` number, nullable, required
    - `ageMin` number, nullable, required: Each bound is independently optional, so "35+" is expressible.
    - `budget` number, nullable, required: Whole units of the **plan’s** currency.
    - `campaignPlanId` string, required
    - `channel` 'meta' | 'tiktok' | 'snapchat' | 'google_search' | 'google_pmax' | 'google_display' | 'youtube' | 'programmatic_display' | 'linkedin' | 'x', required: The planner’s word for what is being bought, not a provider name. A plan may name a channel we cannot execute.
    - `countries` string[], required
    - `createdAt` number, required: Milliseconds since the Unix epoch, UTC.
    - `createdBy` string, required
    - `effectiveEndDate` string, nullable, required: The line’s own bound where it has one, else the plan’s.
    - `effectiveStartDate` string, nullable, required: A calendar date, `YYYY-MM-DD`.
    - `endDate` string, nullable, required: A calendar date, `YYYY-MM-DD`.
    - `executable` boolean, required: Whether this channel could be executed at all today. Derived from our capabilities, never a promise that it will be.
    - `executions` object[], required: What this line has spawned. Never a claim that it is *finished* — nobody declared how many objects a line should produce.
      - `advertiserId` string, required
      - `createdAt` number, required: Milliseconds since the Unix epoch, UTC.
      - `entityId` string, required: The provider’s own id, unprefixed, so it joins to `entities`.
      - `entityType` string, required: The provider’s word for the level — `campaign`, `adset`, `ad`.
      - `id` string, required
      - `mutationWorkflowId` string, nullable, required: The durable execution that performed the write, when known.
      - `provider` string, required
    - `gender` 'all' | 'men' | 'women', nullable, required
    - `id` string, required
    - `objective` 'awareness' | 'engagement' | 'leads' | 'sales' | 'traffic' | 'app_promotion', required
    - `personaId` string, nullable, required: Explains the targeting; never supplies its values.
    - `provider` string, nullable, required: Who sells this channel, or null when nobody we integrate with does.
    - `revision` number, required: Increments on every write. Send it back as `base_revision` to edit; a stale one answers 409.
    - `startDate` string, nullable, required: A calendar date, `YYYY-MM-DD`.
    - `status` 'draft' | 'validated', required: `validated` is the gate on execution.
    - `unit` 'total' | 'daily', required: How `budget` reads, derived from the effective end date. Present → a total for the period; absent → a daily rate. A surface must print this beside the amount.
    - `updatedAt` number, nullable, required: Milliseconds since the Unix epoch, UTC.
  - `measurement` string, nullable, required
  - `name` string, required
  - `rationale` string, nullable, required
  - `revision` number, required: Increments on every write. Send it back as `base_revision` to edit; a stale one answers 409.
  - `slug` string, required
  - `startDate` string, nullable, required: A calendar date, `YYYY-MM-DD`.
  - `status` 'draft' | 'approved', required: The only two stored states. `active`, `closed` and "executed" are derived, never stored.
  - `totalBudget` number, nullable, required
  - `updatedAt` number, nullable, required: Milliseconds since the Unix epoch, UTC.

## Other responses

- `400`: The request does not match the schema of this operation: a field is missing or has the wrong type, or the body is not valid JSON. `error` is `invalid_request`, and `issues` names each field. Nothing was changed.
- `401`: No API key, or one that does not resolve. See the security scheme. `error` is `unauthorized`.
- `402`: The organization has no active subscription. An owner subscribes at `paywallUrl`. `error` is `organization_unentitled`.
- `403`: The caller does not hold `campaign_plan:read`. `error` is `forbidden`.
- `404`: No Campaign Plan with that slug. `error` is `not_found`.

---

[API](https://skmtc.dev/adcrunch/apis/adcrunch-api.md) · [All operations](https://skmtc.dev/adcrunch/apis/adcrunch-api/llms.txt) · [OpenAPI document](https://skmtc.dev/adcrunch/apis/adcrunch-api/revisions/a420ba80914f?raw)
