Tempio Docs

How Tempio paces and allocates budget

What a campaign was planned to spend each day, how that becomes a pace and one of five states, and how envelopes and phases become a daily set of budget moves funded under the workspace's cap. Every figure is computed at read time, nothing is stored, and every move is a change request before it is a change.

Last reviewed 2026-09-19.

Pacing is one comparison: what a target was planned to spend by a day against what it spent. Nothing is stored — every figure is computed at read, so a bar, a chip, a tool result and a draft's report cannot disagree.

Planned spend per day

Three sources in order; the first with an answer wins, and every result says which it used.

  1. Envelope × phase. The enabled envelope's peak daily amount scaled by the phase covering that day, and by the whole envelope where no phase does.
  2. A plan item's whole-flight budget over the inclusive flight length.
  3. The campaign's own daily budget, labelled as the platform's number.

Outside the flight there is no plan, and a live campaign with neither an envelope nor a daily budget has none at all. Both answer null, and null is not zero.

Pace and the five states

Both sides sum from the flight's start to the last complete day, the reporting day minus one. Today is partial everywhere, so counting it would paint every campaign behind.

pace = actual to date ÷ planned to date, null on a zero denominator.

  • on_pace — within ten percent of 1.0; the band is wide because delivery swings by more than that on its own.
  • behind — below 0.9. ahead — above 1.1.
  • over_cap — the account spent more yesterday than its resolved guardrail cap allows: the account's fact, carried onto every campaign under it, said first.
  • unknown — nothing to compare against.

The null rule

No rows is unmeasured. It is never zero. A zero denominator has no ratio, so pace is null and the state is unknown; a campaign that spent nothing against a real plan paces at 0 and reads behind. Never report either as the other.

The projection

Actual to date plus the remaining planned days extended at the measured factor: where the flight lands if this pace holds, labelled a projection. No pace means nothing to project — null, never the plan restated as a forecast. The factor is clamped at 3, past which it is almost always a data problem, and the result says so.

Goals

A number a target is held to, measured from the same daily rows over a trailing window of up to a year. Metrics cpa, roas, ctr, cpm, spend; direction at_most or at_least; flag shows the state, propose also feeds the allocator. Units are minor units for cpa, cpm and spend and basis points for roas and ctr, where 10,000 is 1.0.

met is null when nothing could be measured — never false, which reads as measured and missed. margin is how far the actual beats the target as a signed fraction of it, null when the actual is null or the target zero.

The allocator

Off until a person turns it on, with a run hour in the workspace's zone. An envelope is a ceiling per campaign, never a target, set only by an owner or admin in the app; a phase is a dated window scaling it, 0–300 percent.

It refuses before it runs when the workspace's cap resolves at the default level: funding a workspace under a number nobody chose is a decision nobody made. Nothing is written, the day is not burned, and a dry run still answers, with capSource.

One deterministic pass:

  1. Desire. Envelope × today's multiplier; no phases is the whole envelope, every phase ended is zero.
  2. Goals. A missed propose goal holds the campaign at today's budget, never above; beating an at_most propose goal by over 20 % makes it raise-eligible. Under seven days with spend, goals say nothing.
  3. Order. closing, launch, custom, hold, then no phase; within a kind, raise-eligible, priority (lower first), margin (best first), id.
  4. Fund down that order under the workspace cap and each account's own. A campaign in another currency is held to its account's cap alone and named, never converted.
  5. Clamp to the maximum movement, round to the minor unit, drop moves under 1 % or 100 minor units. Zero is never drafted — that is a pause, and a pause is a person's act.
  6. Draft update_budget change requests, one batch per connection, at most fifty moves in a run: past that a campaign reads batch_limit and is left for tomorrow.

Only campaigns budgeted at campaign level move. Every one gets a reason: funded, throttled, phases_ended, goal_missed, unchanged, below_floor, clamped, no_current_budget, no_account_cap, cannot_zero, batch_limit, budget_not_at_campaign.

What propose_reallocation returns

The same pass on demand. With dryRun it answers the moves, the throttles and the report and writes nothing; without it, it drafts under the same once-a-day ledger. Neither form applies anything — report what it produced as change requests at the gate, not as budgets that moved.

The ledger is audit rows: planner.run claims the day, planner.drafted records each batch with its correlation id, planner.blocked a connection the gate refused, planner.run_complete what the run produced. The reasoning lives on planner.run and in this answer, never on the batch.

What a person must set first

  • A workspace daily cap, or the allocator refuses.
  • An envelope per campaign it should move.
  • Phases, where budget follows a calendar.
  • The allocator switch and its hour.
  • An approval policy over update_budget, or batches wait at the gate.

Sources

  • plannedForDay, paceOf, projectEndOfFlight, activePhase — the pure arithmetic — repo://apps/app/lib/ads/pacing.ts (read 2026-09-19)
  • getTimeline — the read model every pacing surface draws from — repo://apps/app/lib/ads/timeline.service.ts (read 2026-09-19)
  • allocate — desire, goals, order, fund, clamp — repo://apps/app/lib/ads/allocator.ts (read 2026-09-19)
  • loadAllocatorInput, runAllocator, the planner.* ledger rows — repo://apps/app/lib/ads/allocator.service.ts (read 2026-09-19)
  • evaluateGoal, verdictFrom — goal units and the measured rule — repo://apps/app/lib/ads/goals.service.ts (read 2026-09-19)
  • Pacing — the product page for the same rules — repo://apps/docs/content/docs/plans/pacing.mdx (read 2026-09-19)