Tempio Docs
Campaigns

Campaigns

The three pages

Campaigns is where the structure in your connected accounts is read and changed. Three pages, each one a level deeper:

PageWhat it shows
/campaignsThe list: every campaign your connections mirror, over the last 7, 30 or 90 days, with its channel, status, spend, ROAS, CPA and CTR. Filter it by channel and search it by name
/campaigns/<campaign>One campaign's record. Its details run down the left: status, channel, ad account, objective, daily budget, flight, pacing and pending drafts, under its name and its actions. Four tabs fill the rest: Overview (eight highlights and spend and revenue by day), Ad groups, Ads (every ad in the campaign, with the ad group it sits in) and Activity
/campaigns/<campaign>/ad-groups/<ad group>One ad group's record: its details (status, channel, ad account, campaign, budget, bid and pending drafts) and three tabs, Overview, Ads and Activity. A row in the campaign's Ad groups tab opens it

Every figure on these pages is computed from stored base counts at the moment the page is drawn. CTR, CPA, CPC, CPM and ROAS are ratios, so they are computed per row and left empty where the denominator is zero rather than shown as a zero. Money is the ad account's own currency.

On the list, the range and the channel are chips in the toolbar, under the view and New campaign. On a record, the range sits in the header of each tab that shows figures (Overview, Ad groups and Ads), and the tabs carry it as you move between them. The channel filter is on the campaign list only: below it the channel is already fixed by the account the campaign runs in.

Each table keeps its sort, its hidden columns and their widths in the page's address, so a link you copy opens the same view. Changing the range or the channel starts the view over. View settings shows, hides and reorders the columns; the name column always comes first.

Every action here is a draft

Nothing on these pages writes to an ad platform. A row action opens a dialog, the dialog's one button reads Create draft (Ctrl+Enter, or ⌘+Enter on a Mac, presses it), and what it produces is a change_request waiting at the gate — the same row your own AI over MCP and the in-app agent produce, with the same guardrails, currency stamping and audit trail. It is applied when it is approved, by you or by a rule you wrote. Drafts is where that happens.

LevelWhere the actions areWhat it can offer
CampaignThe menu beside its name on the list, and the action row under its name on its recordPause, Resume, Edit budget
Ad groupThe menu beside its name in the campaign's Ad groups tab, and the action row on its own recordPause, Resume, Edit budget, Edit bid, Rename
AdThe menu beside its name in an Ads tab, on the campaign's record or the ad group'sPause, Resume, Rename

A row's menu sits at the end of its name and shows while the row is under the pointer or holds the focus. With the keyboard, Enter on a campaign or an ad group opens its record, where the same actions are buttons; an ad has no record, so Enter on its row opens its menu. On a phone each row is a card, and its menu is the button in the card's corner.

An ad carries no budget and no bid of its own — those live on the ad group — so two of the four ever reach an ad row, and what the platform can express decides even that.

Those are one-field changes. Schedules, targeting, creative and removals are their own surfaces and are drafted through the agent or over MCP; the row menu stays the four verbs it can put in a dialog.

Pause and Resume are one item that reads whichever of the two the row can currently take: an active row offers Pause, a paused one offers Resume. Archiving is not offered below the campaign — Google and TikTok refuse archived on an ad group or an ad — and it is a different decision from un-pausing wherever it can be expressed, so it is not a row action.

After a draft is written the page you fired it from is redrawn, so the record's Drafts count and the state of the menu are the gate's answer, not the one the page loaded with.

A row offers only what its platform can express

The items in a row's menu are not a fixed list with some of them greyed out. They are the intersection of two facts, computed server-side per row:

  • what the platform's adapter says it implements, for the level the row is at — including where that platform keeps budgets;
  • what the operation means at that level at all.

So a Google ad group shows no Edit budget, because a Google budget attaches to the campaign and an ad group there has none of its own. A Google ad shows no Rename, because a responsive search ad's name is not settable and the adapter refuses the write. A row whose platform can express none of the four renders no menu at all, rather than a menu that promises what the row cannot keep.

The same rule shapes the create form: the objective list is the connection's own list of objectives, and the ad group's budget field is rendered only where that platform puts budgets on ad groups. Where it does not, the form says so in a line instead of offering a field the platform has nowhere to put.

A pending draft holds the door

One draft per operation per row. While a change request for that row and that operation is waiting at the gate, the row's menu item keeps its name and adds (draft pending), as in Pause (draft pending), and is disabled. On a campaign's or an ad group's own page the button for that change is disabled instead, and its tooltip says a draft for this is waiting for approval. If the page's read was older than the gate, the dialog is disabled too, with a line saying a draft for this is already at the gate and to approve or reject it before drafting another.

A second draft of the same operation would only queue a contradiction: two budgets for one ad group, applied in whatever order the gate cleared them. The pending row holds the door until it is approved or rejected, and then the row is open again.

Money

Amounts are typed the way they are read — 50.00, 1500, 2.50 — and parsed on the server against the currency of the ad account that owns the row, never against a currency the form supplied. An account in yen has no minor digits, so its fields seed and hint without them; an account in dollars gets two. What is stored is an integer in that account's minor units with the currency stamped beside it.

The budget dialog changes the amount, not the kind of budget: a row already running a lifetime budget drafts a new lifetime amount, and the mode comes from the mirrored row rather than the form. A daily budget retyped as a lifetime one is a different flight, which is a different change.

The bid dialog moves the amount and states the strategy the ad group already runs on. Which strategies a row could take is not something a platform's capabilities declare, so the dialog does not offer a picker built on a guess.

Budget drafts are checked against the account's guardrails — the per-account daily cap, the 100% rule, the 24-hour aggregate — when they are written, again at approval, and once more immediately before the platform is called. Drafts has the whole of that.

Creating a campaign, its ad group and its ad

New campaign opens as a dialog over the list, and as a page of its own when you follow a link to /campaigns/new or reload it. It is three steps over one form, each a tab: the campaign, its ad group, its first ad. Every step stays in the form while you move between them, and one submit carries all three. Ctrl+Enter (⌘+Enter on a Mac) presses the footer's button: Next on the first two steps, Create draft on the last.

  1. The campaign. The ad account it runs in — which decides the currency, the connection and what the two steps after it can express — a name, an objective from that connection's own list, and a daily budget.
  2. Its ad group. A name, and at least one country: every platform asks where the ads run, so the countries field is the one part of targeting that is not optional. Two-letter codes, comma separated. An age band, an audience, an ad-group budget where the platform keeps budgets there, and a bid are each optional.
  3. Its first ad. A name, headlines one per line, descriptions one per line, the URL a click goes to, and — where the account's media library has something for it — an asset from that library. Who the ad posts as comes from the account's own defaults, not from this form.

Create draft writes three change requests: the campaign, the ad group under it, the ad under that. They share one correlation, they carry their order in the run, and they are reviewed and approved as a single unit. Neither child names its parent's platform id, because that id does not exist until the run creates it; each one takes it from the row before it once that row has actually landed. You are taken straight to the group at the gate.

Campaign only drafts the campaign alone, from the four fields the first step already holds — one change request, same gate.

A refusal keeps everything you typed: the form comes back with the batch intact, one sentence saying what happened, what did not, and what to do next. Nothing is drafted when a step is refused — not the campaign, not part of the structure.