Skip to main content
A campaign is where a buyer’s plan lives. It holds the brief sellers read, the flight dates and budget, any targeting, the creatives that run, and the media buys placed with each seller. Every campaign belongs to exactly one advertiser and inherits its currency. A campaign starts as a DRAFT. The draft is your cart: you add proposals and products to it and stage media buys against it without contacting any seller. Launching turns the staged buys into real AdCP transactions, one per seller.

Key fields

string
required
Stable id with the cmp_ prefix.
integer
required
The advertiser that owns the campaign. The budget currency must match the advertiser’s primary currency.
string
required
Display name, 1 to 200 characters.
string
The plain-language description sellers read: goals, audience, channels, must-haves and constraints. Up to 20,000 characters. The better the brief, the better the proposals.
enum
DRAFT, ACTIVE, PAUSED, COMPLETED, CANCELED or ARCHIVED. See lifecycle.
enum
managed for campaigns authored in Semicola, which places and runs their buys. Every campaign you create today is managed. The value tracked is reserved for campaigns mirrored read-only from a connected ad platform account; mirroring isn’t available yet, so no campaign is tracked and the campaign list’s Tracking lens stays empty.
object
startAt and endAt, both ISO 8601 date-times with an offset.
object
total (decimal), currency (ISO 4217), pacing (EVEN, ASAP or FRONTLOADED) and an optional dailyCap. Every package staged on the campaign is sent to its seller with the matching pacing (even, asap or front_loaded); change a single package’s pacing with update_media_buy.
decimal string
How much of the total is committed to staged or live media buys, and how much is left. Allocated budget can never exceed the total.
object
Structured intent sent with the brief: countries, channels, ageRange (min 13 or more, max 99 or less), audience (free text) and geoMetros. Sellers that cannot honor part of it say so in their proposal.
object
How independently Semi works on the campaign: inventorySelection and rebriefing, each manual, propose or automatic. Without a value, a new campaign copies its advertiser’s setting. With inventorySelection: "automatic", manual staging is refused and products come from auto-select. The campaign workspace shows it under Controls as Inventory selection and Re-briefing, marked when it differs from the advertiser. See Autonomy and auto-select.
object
Holds optimizationGoals: the ranked goals sellers optimize toward, primary first, 1 to 10. Set them with save_campaign (optimizationGoals) or REST performanceConfig. See Optimization goals.
array
Coherent inventory selections (for example mobile web display and CTV). Each group becomes its own media buy per seller, and delivery can be reported by group. See Channel groups.
array
Buyer-side caps (max_impressions per window). Stored, never sent to sellers and not enforced; see Frequency caps.
object
Values from your account’s dimensions, such as { "market": ["us"], "tags": ["launch"] }. Set with save_campaign; filter campaigns by them. See Dimensions and labels.
object
Format coverage for launch: required, covered and missing.
array
The campaign’s media buys, one per seller (and settlement currency). See media buys and packages.
integer
required
Increments on every change. Writes that modify a campaign send expectedRevision; a stale value fails with REVISION_CONFLICT so two agents never overwrite each other.

Optimization goals

A goal tells each seller what to optimize the campaign’s packages toward. Goals are opt-in: set them when the buyer states one. There are two kinds. Event goals (kind: "event") optimize toward conversions from the advertiser’s event sources: Metric goals (kind: "metric") optimize toward a delivery metric the seller tracks: metric is one of clicks, views, completed_views, viewed_seconds, attention_seconds, attention_score, engagements, follows, saves, profile_visits or reach, with optional viewDurationSeconds, a target (cost_per or threshold_rate, each with a value), attributionWindow and priority. A campaign’s reach goal takes no reachUnit: sellers need one, so each package is sent the first reach unit its product declares, and a package whose product declares none is sent without the reach goal.
  • Set. save_campaign takes optimizationGoals on create and on update. Over REST, send performanceConfig: { "optimizationGoals": [ … ] } on POST /campaigns, PUT /campaigns/{id} or campaign.create in the media-buy batch (POST /media-buys/batch).
  • Change. A list replaces the whole ranked list; omitting the field keeps the current goals; null clears them (REST: performanceConfig: null). An empty list is refused. A change is recorded in the campaign’s activity as Optimization goals.
  • Validation. Every eventSourceId must be a registered source of the campaign’s advertiser. Otherwise the write fails with VALIDATION_ERROR on optimizationGoals, naming the missing keys in details.unknownEventSourceIds. Register sources first (see Event sources). Each eventType must be one the source sends (its eventTypes). Otherwise the write fails the same way, and each mismatch is listed in details.unsupportedEventTypes with eventSourceId, eventType and the source’s sourceEventTypes.
  • Sent. When a buy goes to its seller, every package carries the ranked goals as AdCP optimization_goals. Each event source is replaced by the seller’s own id for it; an event goal whose source isn’t provisioned on that seller, or whose seller has Events turned off in Connections, is left out of that buy, and the buy goes ahead. See How a source reaches each seller.
  • Live buys. Changing the goals on a live campaign sends the new ranked list to each of its ACTIVE and PAUSED buys with update_media_buy (packages[].optimization_goals; null sends an empty list). The response carries optimizationGoalsCascadeResult: totalMediaBuys, updatedCount, pendingCount, skippedCount, failedCount and one results entry per buy (outcome updated, pending, skipped or failed, with a reason). pending means the seller must approve first; poll its updateProposalId with get_update_proposal. A buy is skipped when Buy is off for its seller, when a change is already waiting for that seller’s approval, or when it has no seller-side buy. Draft buys pick up the goals when they’re sent.
Not available yet: goals per media buy or per product, and showing goals in the campaign workspace (read them from performanceConfig).

Lifecycle

Pausing a campaign pauses every media buy on it. Reactivating is the only way back from PAUSED.

Launch preconditions

A draft can launch when all of these hold:
  • the advertiser is active and the budget currency matches its primary currency;
  • at least one media buy is staged;
  • allocated budget does not exceed the total;
  • the required creative formats are covered, or you accept that some buys will wait for creatives;
  • the account is allowed to buy live (otherwise only sandbox launches are possible).
The Review & go live widget (open_campaign_receipt) shows each of these as a checklist with any blockers.

Common operations

Create a draft

If a required fact is missing (most often the currency), the tool answers with needs_input and a question instead of guessing. Ask the person, then call again.

Collect proposals and stage buys

  1. request_proposals with campaignId and expectedCampaignRevision sends the brief to every eligible seller and returns an execution id straight away. Only one execution runs per buyer at a time.
  2. Poll with get (kind: "proposal") or watch the Proposals widget as sellers answer.
  3. refine_proposal asks one seller for a revised version.
  4. save_media_buy with fromProposalId stages the proposal’s products as a draft media buy.

Launch

Launching is deliberately two calls so a person can see the spend before it happens.
1

Ask to go live

Call save_campaign with campaignId, expectedRevision and desiredPhase: "active". The result is pending_confirmation, with a summary of spend per seller and an expiry.
2

Get the person's approval

Show the summary. In the Semicola app and in MCP Apps hosts, a confirmation card does this for you.
3

Confirm

Call save_campaign again with confirmLaunch: true and the same expectedRevision. Semicola dispatches one AdCP create_media_buy per seller, each with its own idempotency key.
A brief, a budget or a staged buy is never authority to spend. Agents must not set confirmLaunch without an explicit approval from the person they act for.
The launch result reports mediaBuysExecuted and a list of errors, one per failed buy, with a recovery of transient, correctable or terminal and a retrySafe flag. Launching again resubmits only the failed draft buys.

Pause, reactivate, cancel, archive

Send isPaused, desiredPhase: "canceled" or isArchived to save_campaign with expectedRevision. Over REST, use POST /api/v2/buyer/campaigns/{id}/pause and /reactivate.

Media buys and packages

A media buy is one AdCP transaction with one seller in one settlement currency. Its id starts with mb_. A package (pkg_) is one product inside a media buy, with its own budget, pacing (even, asap or front_loaded), optional bid price, flight and creatives. The campaign’s operational status is the most restrictive status across its buys: one of no_media_buys, draft, pending_creatives, pending_start, active, paused, completed or attention_required. Read it from GET /api/v2/buyer/campaigns/{id}/media-buy-status or the campaign workspace.

Buying overview

The whole loop from brief to delivery.

v3 Tool Catalog

Exact inputs for save_campaign, request_proposals and save_media_buy.

Errors

Revision conflicts, needs_input and partial launches.

Glossary

Every term on this page, defined.