Create a Campaign Plan
Creates a Campaign Plan. A Campaign Plan is the AdCrunch planning document. It is not a provider campaign. For provider campaigns, see the entities in the Observe API.
Only a name, a description and a currency are necessary. You write the window, the budget envelope and the Brand one part at a time. Write each amount as an integer in the minor unit of the currency, for example cents.
The new plan starts in draft. The answer is 409 if the slug already exists in your organization.
/context/campaign-plans/AuthorizationBearer token · headerrequiredSend Authorization: Bearer <credential>. Two credentials work.
An API key (acr_…) is what a server integration uses. Create one in the AdCrunch console under Settings → API keys — see https://docs.adcrunch.dev/account/api-keys. A key is bound to the organization that was active when you created it, and it keeps acting on that organization whatever you do later. It carries the permissions of the member who created it.
A session issued by the AdCrunch sign-in flow also works, which is how the console calls this service from the browser.
Either way the organization comes from the credential. There is no organization parameter.
application/jsonbrand_namestringThe Brand's slug. Optional and re-pointable — a plan may precede its brand or cover a portfolio. Addressed by slug because that is the handle every Define surface uses; the plan stores the id.
currencystringrequiredISO 4217. Editable while no Line Item carries a budget, frozen thereafter.
descriptionstringrequiredOne-line summary — what a reader picks this plan by.
end_datestringOmit for an always-on plan. Line Items inherit this.
measurementstringHow success will be judged (markdown).
namestringrequiredDisplay name.
rationalestringThe argument for the split — what a reviewer approves (markdown).
slugstringOptional explicit slug handle; normalized to kebab-case. Defaults to one derived from the name.
start_datestringtotal_budgetintegerThe envelope: what we have. Omit for "no declared constraint" — which is not the same as zero.
The Campaign Plan as created, in draft, with no rows.
allocationobjectrequiredDerived on every read. Never stored.
Show propertiesHide properties
allocatednumber | nullrequiredThe sum of the Line Item budgets. null when the lines mix units, because that sum would be meaningless.
comparablebooleanrequiredFalse when the lines mix totals and daily rates. A surface must then print "not comparable" rather than a plausible-looking wrong total.
unallocatednumber | nullrequiredThe envelope less the allocated, where both are known.
unitstring | nullrequiredThe shared unit, when the lines agree on one.
totaldailybrandIdstring | nullrequiredNullable and mutable: a plan may precede its brand, cover a portfolio, or be re-pointed. Deleting a Brand leaves its plans standing.
createdAtnumberrequiredMilliseconds since the Unix epoch, UTC.
createdBystringrequiredcurrencystringrequiredEditable until the first Line Item budget, and frozen thereafter.
descriptionstringrequiredendDatestring | nullrequiredA calendar date, YYYY-MM-DD.
idstringrequiredlineItemsobject[]requiredShow propertiesHide properties
objectadvertiserIdstring | nullrequiredageMaxnumber | nullrequiredageMinnumber | nullrequiredEach bound is independently optional, so "35+" is expressible.
budgetnumber | nullrequiredInteger minor units of the plan’s currency.
campaignPlanIdstringrequiredchannelstringrequiredThe planner’s word for what is being bought, not a provider name. A plan may name a channel we cannot execute.
metatiktoksnapchatgoogle_searchgoogle_pmaxgoogle_displayyoutubeprogrammatic_displaylinkedinxcountriesstring[]requiredcreatedAtnumberrequiredMilliseconds since the Unix epoch, UTC.
createdBystringrequiredeffectiveEndDatestring | nullrequiredThe line’s own bound where it has one, else the plan’s.
effectiveStartDatestring | nullrequiredA calendar date, YYYY-MM-DD.
endDatestring | nullrequiredA calendar date, YYYY-MM-DD.
executablebooleanrequiredWhether this channel could be executed at all today. Derived from our capabilities, never a promise that it will be.
executionsobject[]requiredWhat this line has spawned. Never a claim that it is finished — nobody declared how many objects a line should produce.
Show propertiesHide properties
objectadvertiserIdstringrequiredcreatedAtnumberrequiredMilliseconds since the Unix epoch, UTC.
entityIdstringrequiredThe provider’s own id, unprefixed, so it joins to entities.
entityTypestringrequiredThe provider’s word for the level — campaign, adset, ad.
idstringrequiredmutationWorkflowIdstring | nullrequiredThe durable execution that performed the write, when known.
providerstringrequiredgenderstring | nullrequiredallmenwomenidstringrequiredobjectivestringrequiredawarenessengagementleadssalestrafficapp_promotionpersonaIdstring | nullrequiredExplains the targeting; never supplies its values.
providerstring | nullrequiredWho sells this channel, or null when nobody we integrate with does.
revisionnumberrequiredIncrements on every write. Send it back as base_revision to edit; a stale one answers 409.
startDatestring | nullrequiredA calendar date, YYYY-MM-DD.
statusstringrequiredvalidated is the gate on execution.
draftvalidatedunitstringrequiredHow 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.
totaldailyupdatedAtnumber | nullrequiredMilliseconds since the Unix epoch, UTC.
measurementstring | nullrequirednamestringrequiredrationalestring | nullrequiredrevisionnumberrequiredIncrements on every write. Send it back as base_revision to edit; a stale one answers 409.
slugstringrequiredstartDatestring | nullrequiredA calendar date, YYYY-MM-DD.
statusstringrequiredThe only two stored states. active, closed and "executed" are derived, never stored.
draftapprovedtotalBudgetnumber | nullrequiredupdatedAtnumber | nullrequiredMilliseconds since the Unix epoch, UTC.
No credential, or one that does not resolve. Send an API key or a session. See the security scheme. error is unauthorized.
errorstringrequiredA stable code for the failure. This is the field to branch on. It does not change for a given failure.
unauthorizedmessagestringrequiredA sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
The caller does not hold campaign_plan:write. error is forbidden.
errorstringrequiredA stable code for the failure. This is the field to branch on. It does not change for a given failure.
forbiddenmessagestringrequiredA sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
brand_name names no Brand in this organization. error is not_found. Despite its name the field takes a Brand slug.
errorstringrequiredA stable code for the failure. This is the field to branch on. It does not change for a given failure.
messagestringrequiredA sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
Another Campaign Plan already holds that slug. error is slug_conflict.
slugstringrequiredThe slug that is already taken.
errorstringrequiredA stable code for the failure. This is the field to branch on. It does not change for a given failure.
slug_conflictmessagestringrequiredA sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.
Four codes share this status. invalid_slug; invalid_currency (it must be a three-letter ISO 4217 code); invalid_amount (amounts are whole minor units, never negative); invalid_window (the end precedes the start).
errorstringrequiredA stable code for the failure. This is the field to branch on. It does not change for a given failure.
messagestringrequiredA sentence to show a person. Written to say what to do next. Reworded whenever it can be said better, so never branch on it.