CLI
tempio is the same scoped surface from a terminal. It is an MCP client: it
talks to the MCP server with a key you mint, builds its commands
from the route table that server publishes, prints the result envelope the
server returns, and exits with a code that says what happened.
It adds nothing of its own. Every command is one call an agent holding the same key could make, every write becomes a change request before it becomes a change, and guardrails and the audit log sit under the tool layer rather than in any of its callers — so a change made from a shell passes exactly what a change made in the app passes.
Install
The binary is tempio, and it needs Node 20 or newer. The publish credential
for the npm organisation is still outstanding, so until it exists each release
carries the built tarball as an asset and npm i -g ./tempio-cli-<version>.tgz
installs the same binary.
npm i -g @tempio/cli # keep it
npx @tempio/cli --help # or run it without installing
tempio --versionSign in
The credential is an MCP key, minted under Settings → MCP
with the scopes you choose: read, draft, approve.
tempio login # prints the URL, then reads the key with no echo
tempio whoami # the workspace, the scopes, the surface, the key's prefix
tempio logout # forget this endpoint's keylogin takes no --key flag, because a key on a command line is a key in a
shell history: it reads the key from the prompt, or from a pipe
(tempio login < key.txt). It then verifies the key with one call before
storing it, so a key pasted with a newline in it fails at the prompt rather
than on your first real command.
The file is $XDG_CONFIG_HOME/tempio/credentials.json — ~/.config/tempio by
default, %APPDATA%\tempio on Windows — mode 0600, keyed by the endpoint's
origin, so one machine can hold a staging key and a production key at once.
What a call uses, in order:
| The key | The endpoint |
|---|---|
--key | --endpoint |
TEMPIO_API_KEY | TEMPIO_ENDPOINT |
| the credential file's entry for that endpoint's origin | https://app.tempio.ai/api/mcp |
No workspace identifier is ever sent and no --organization-id flag exists:
the workspace comes off the verified credential. There is no --surface flag
either — the CLI always calls tempio_manage, which every key can reach.
The commands
One command per row of the route table, tempio <entity> <action> [flags],
generated rather than written, so a capability that lands is a command with no
release of its own. A command whose scope your key does not hold is refused by
name, with the scope it would need, rather than hidden.
tempio --help # every entity, and its actions
tempio <entity> --help # the actions on one entity
tempio <entity> <action> --help # the flags, typed, required ones marked| Entity | Command | Scope | Flags |
|---|---|---|---|
connection | tempio connection list | read | no flags |
account | tempio account list | read | --connection-id |
tempio account update | draft | --connection-id* --target-entity-id --payload* | |
campaign | tempio campaign list | read | --ad-account-id --platform |
tempio campaign get | read | --campaign-id* | |
tempio campaign create | draft | --connection-id* --payload* | |
tempio campaign update | draft | --connection-id* --target-entity-id --operation* --payload* | |
tempio campaign remove | draft | --connection-id* --target-entity-id --payload* | |
ad_group | tempio ad_group list | read | --parent-id --limit |
tempio ad_group create | draft | --connection-id* --payload* | |
tempio ad_group update | draft | --connection-id* --target-entity-id --operation* --payload* | |
tempio ad_group remove | draft | --connection-id* --target-entity-id --payload* | |
ad | tempio ad list | read | --parent-id --campaign-id --limit |
tempio ad create | draft | --connection-id* --payload* | |
tempio ad update | draft | --connection-id* --target-entity-id --operation* --payload* | |
tempio ad remove | draft | --connection-id* --target-entity-id --payload* | |
creative | tempio creative list | read | --parent-id --limit |
tempio creative create | draft | --connection-id* --payload* | |
tempio creative update | draft | --connection-id* --target-entity-id --payload* | |
keyword | tempio keyword list | read | --parent-id --limit |
tempio keyword create | draft | --connection-id* --payload* | |
tempio keyword update | draft | --connection-id* --target-entity-id --payload* | |
tempio keyword remove | draft | --connection-id* --target-entity-id --payload* | |
negative_list | tempio negative_list create | draft | --connection-id* --payload* |
tempio negative_list update | draft | --connection-id* --target-entity-id --payload* | |
asset_group | tempio asset_group create | draft | --connection-id* --payload* |
tempio asset_group update | draft | --connection-id* --target-entity-id --payload* | |
lead_form | tempio lead_form create | draft | --connection-id* --payload* |
media | tempio media list | read | --ad-account-id --kind --limit |
tempio media create | draft | --connection-id* --payload* | |
draft | tempio draft list | read | --correlation-id --status --limit |
tempio draft get | read | --correlation-id | |
tempio draft create | draft | --connection-id* --operations* | |
tempio draft update | draft | --draft-id* --payload* | |
tempio draft approve | approve | --draft-ids --correlation-id | |
tempio draft reject | approve | --draft-ids --correlation-id --note | |
tempio draft revert | draft | --draft-id* | |
plan | tempio plan list | read | no flags |
tempio plan get | read | --plan-id* | |
tempio plan create | draft | --name* --brief --start-date* --end-date* --currency | |
tempio plan update | draft | --plan-id* --name --brief --start-date --end-date --currency --status | |
tempio plan materialize | draft | --plan-id* --promote | |
plan_item | tempio plan_item create | draft | --plan-id* --kind --platform* --name* --objective --budget --start-date* --end-date --ad-account-id --sort-order |
tempio plan_item update | draft | --plan-id* --item-id --kind --platform* --name* --objective --budget --start-date* --end-date --ad-account-id --sort-order | |
tempio plan_item remove | draft | --plan-id* --item-id* | |
metrics | tempio metrics list | read | --from* --to* --platforms |
campaign_metrics | tempio campaign_metrics list | read | --from* --to* --platforms |
brief | tempio brief get | read | --ad-account-id |
research | tempio research search | read | --connection-id* --kind* --seeds* --language --geo-target-ids --limit |
geo | tempio geo search | read | --connection-id* --query* --country-code --locale --limit |
search_term | tempio search_term list | read | --connection-id* --date-range* --campaign-id --ad-group-id --limit |
placement | tempio placement list | read | --connection-id* --date-range* --level --campaign-id --ad-group-id --limit |
change_event | tempio change_event list | read | --connection-id* --since* --until* --campaign-id --limit |
platform_request | tempio platform_request get | read | --connection-id* --method* --path* --query --body --description |
tempio platform_request mutate | draft | --connection-id* --method* --path* --query --body --description* | |
library_ad | tempio library_ad get | read | --ad-id* |
tempio library_ad search | read | --query --platform --status --format --language --advertiser --min-days --max-days --min-score --min-spend --ids --sort --limit --cursor --seed | |
library_advertiser | tempio library_advertiser list | read | --query --industry --category --tag --source --limit --cursor |
tempio library_advertiser get | read | --advertiser-id* | |
tempio library_advertiser create | draft | --website --name --library-url | |
tempio library_advertiser search | read | --query --industry --category --tag --source --similar-to --limit --cursor | |
library_watch | tempio library_watch list | read | no flags |
tempio library_watch create | draft | --advertiser-id* --watching --alerts --digest | |
tempio library_watch update | draft | --advertiser-id* --watching --alerts --digest | |
tempio library_watch remove | draft | --advertiser-id* | |
swipe | tempio swipe list | read | --collection --limit --cursor |
tempio swipe create | draft | --ad-id* --collection --note | |
tempio swipe update | draft | --ad-id* --collection --note | |
tempio swipe remove | draft | --ad-id* | |
swipe_collection | tempio swipe_collection list | read | --collection --limit --cursor |
tempio swipe_collection create | draft | --ad-id* --collection --note | |
tempio swipe_collection update | draft | --ad-id* --collection --note | |
tempio swipe_collection remove | draft | A swipe collection is not removed on its own: it exists while ads are saved into it. Remove the ads with update_swipe and `remove: true`, one adId at a time. | |
playbook | tempio playbook list | read | --platforms |
tempio playbook get | read | --slug* --section | |
guardrail | tempio guardrail list | read | --connection-id |
tempio guardrail get | read | --ad-account-id* | |
approval_chain | tempio approval_chain list | read | no flags |
tempio approval_chain get | read | --chain-id | |
connection_permission | tempio connection_permission list | read | --connection-id |
allocation | tempio allocation propose | draft | --dry-run |
timeline | tempio timeline get | read | --from --to --platforms --ad-account-ids --plan-id |
pacing | tempio pacing get | read | --campaign-id --plan-item-id |
phase | tempio phase list | read | --campaign-id --plan-item-id |
tempio phase create | draft | --campaign-id --plan-item-id --kind* --label --start-date* --end-date* --multiplier-pct* --note | |
tempio phase update | draft | --campaign-id --plan-item-id --id --kind* --label --start-date* --end-date* --multiplier-pct* --note | |
tempio phase remove | draft | --phase-id* | |
envelope | tempio envelope list | read | --campaign-ids --plan-item-ids |
goal | tempio goal create | draft | --campaign-id --plan-item-id --metric* --direction* --target* --window-days --action --note |
tempio goal update | draft | --campaign-id --plan-item-id --id --metric* --direction* --target* --window-days --action --note | |
tempio goal remove | draft | --goal-id* | |
brand_kit | tempio brand_kit get | read | --ad-account-id |
tempio brand_kit update | draft | --ad-account-id --name --logo-media-asset-id --logo-blob-url --roles --typography --voice --compliance-line --forbidden-words --products --source-url | |
tempio brand_kit scrape | read | --url* | |
preset | tempio preset list | read | --kind |
creative_asset | tempio creative_asset list | read | --preset-key --status --limit |
tempio creative_asset create | draft | --preset-key* --brand-kit-id --fields* --product-labels --reference --aspects --quantity --verb --instructions --model-id | |
tempio creative_asset resize | draft | --asset-id* --aspect* | |
tempio creative_asset register | draft | --asset-id* --aspect* --ad-account-id* | |
creative_batch | tempio creative_batch get | read | --batch-id* |
lane_colour | tempio lane_colour update | draft | --campaign-ids --plan-item-ids --colour* |
lane_order | tempio lane_order update | draft | --lane* --before --after |
plan_dependency | tempio plan_dependency create | draft | --predecessor* --successor* |
tempio plan_dependency remove | draft | --dependency-id --predecessor --successor | |
key_date | tempio key_date list | read | --from --to --region |
tempio key_date create | draft | --name* --start-date* --end-date --recurs --colour | |
tempio key_date update | draft | --id --name* --start-date* --end-date --recurs --colour | |
tempio key_date remove | draft | --id* |
108 commands over 46 entities. A flag marked * is required. Every command also takes --endpoint, --key, --output and --json.
Flags are the parameters themselves, kebab-cased and typed: a string, a number,
a boolean (--watching and --no-watching), or JSON for an object or an
array. --json '<object>' passes the whole parameter object at once and
is merged under the flags, so a flag wins over it.
A write row takes the form the route documents rather than the batch its tool
takes: --connection-id, --target-entity-id, --payload, and --operation
where the row names more than one. The create row of the draft entity is
the n-ary form instead: --json '{…}' carries one whole batch, with
dependsOn indices between its entries.
A parameter a route supplies itself is not offered as a flag at all, on this
page or in the binary. Each row of the route table publishes the parameters it
reads and the ones it sets itself, and both readers derive their flags from
those two lists, so a flag printed here is a flag the binary takes.
tempio help manage <entity> <action> --full is the authority on what a row
takes, and it is the server answering.
tempio help is that server-side catalogue, not a second copy of it:
tempio help # what this workspace is
tempio help manage <entity> <action> --full # the parameters of one row
tempio help --q budget # search before concluding it is missingOutput
Stdout carries the answer and nothing else. It is JSON when stdout is not a
terminal or when --json is given, and a table when it is a terminal;
--output json and --output table decide it outright either way.
- Money is an integer in the minor units of the currency the answer reports,
printed with the code beside it and divided by nothing.
1234500 AUDis $12,345.00. Metrics is the rule this follows. - A ratio whose denominator was zero is
nullin JSON and—in the table. It is never0. - The deep link to a record is the last column of the table and a field in the JSON, never truncated.
- A write prints
Drafted — review at …and never "applied". Approval is a separate act — yours, or a rule you wrote — and--waitbelow is what earns the other word.
Colour appears only on a terminal, never carries meaning on its own, and
NO_COLOR turns it off.
Warnings, on stderr
Every warning on a result is printed to stderr, one line each, in both output
modes, as [severity] code: message. They are never left buried in a body
nobody is reading: a converted total is an estimate, a figure from a connection
that has not synced carries when it was last refreshed, and a blocking
warning means stop rather than retry. The warning classes are listed on the
MCP reference. In table mode the scope of the answer — its
currency, time zone and date range — is one more stderr line, so stdout stays
pipeable.
Exit codes
A guardrail refusing a change, a connection that is missing and a network that is down are three different outcomes, and they exit as three different things.
| Exit | Code | Retry? | What it means |
|---|---|---|---|
1 | adapter | no | The ad platform refused the call, and its reason is the message. |
apply_failed | no | The change was approved but the platform did not apply it; the draft records what came back. | |
invalid_state | no | The thing you named cannot do this in the state it is in. | |
unknown | no | The call failed for a reason this table does not name. | |
2 | invalid_payload | no | The call was missing something or carried the wrong shape; the message names the field. |
3 | unauthorized | no | No credential was sent, or the server refused the one that was. |
4 | forbidden | no | The credential does not hold the scope this call needs, or you do not hold the permission. |
5 | not_found | no | Nothing matches what you named, here or on the platform. |
6 | guardrail_blocked | no | A guardrail refused the change before it reached the platform. |
7 | plan_limit | no | Your plan's allowance for this is already used up. |
8 | connect_cancelled | no | The connection was never finished because someone backed out of the platform’s consent screen. |
platform_unavailable | yes | The ad platform cannot be reached from here right now. | |
rate_limited | yes | This credential has made too many calls this minute; wait for the window to reset. | |
unavailable | yes | The route is named in the table but no tool answers it yet; the message names the step that lands it. | |
9 | network | yes | The call did not complete because the network failed. |
0 is every command that succeeded with nothing blocking in its warnings. A blocking warning riding along with an answer that otherwise succeeded is the gate refusing, so it exits 6 rather than carrying a number of its own. The stderr line says "retry" exactly where this table does.
TEMPIO_DEBUG=1 adds the underlying cause to the line a network failure
prints. It is a debugging fact rather than a flag.
--wait
A draft is approved and applied moments later, so the answer to an approval is
"claimed", not "applied". --wait — on the approve and create rows of the
draft entity — reads the batch back every two seconds until every row in it
has stopped moving, or until --timeout seconds have passed (120 by default).
The exit code then follows what it last read: every row applied is 0, and
partial, failed or a timeout is 1.
Three worked scripts
A morning read, piped. Output is JSON on a pipe, so jq needs no flag:
tempio campaign_metrics list --from 2026-09-01 --to 2026-09-13 \
| jq '.data.rows[] | select(.spend > 100000) | {name, spend, roas}'A pause drafted from cron, with the exit code read. 6 is the gate
refusing, which is an answer rather than an outage:
#!/bin/sh
tempio campaign update \
--connection-id "$CONNECTION" \
--target-entity-id "$CAMPAIGN" \
--operation update_status \
--payload '{"status":"paused"}'
case $? in
0) echo "drafted; the review link is on stdout" ;;
6) echo "a guardrail refused it — nothing was drafted"; exit 0 ;;
8) echo "come back later"; exit 75 ;;
*) exit 1 ;;
esacA plan materialised, then watched. The first call drafts the whole plan as one batch; the second approves that batch and waits for the platforms:
tempio plan materialize --plan-id "$PLAN" --output json > batch.json
tempio draft approve \
--correlation-id "$(jq -r '.data.correlationIds[0]' batch.json)" \
--wait --timeout 300A key that holds draft but not approve runs the first of those two and is
refused by name on the second, which is the shape a build server should have.