tiktok-ops · git:20260914.e81467a · 2026-09-14 · sha256 1ce2b2931da46dee
tiktok-ops git:20260914.e81467aA
Immutable. This exact content is served forever at /api/v1/blob/1ce2b2931da46dee.
---
name: tiktok-ops
description: "Execute TikTok Ads through the official TikTok for Business MCP server and Marketing API v1.3, with the ttops CLI as an offline guard rail: access and tokens, paused launch, verify-before-activate, budget and currency safety, reporting, automated rules, error codes. Strategy and creative decisions = tiktok-ads."
---
# TikTok Ops
Reviewed 2026-09-14, against TikTok's own docs portal (business-api.tiktok.com/portal/docs).
> **STATUS.** `ttops` passes 102 offline tests and has **never been run against a live advertiser.**
> Its offline commands (`preflight`, `audit`, `budget-check`, `explain`) need no token and are safe
> to trust; its live commands are a paper guarantee until you prove them. Make the first real run a
> throwaway campaign at a floor budget, reviewed by a human before anything is enabled.
## Start here
| You are… | Read | Then |
|---|---|---|
| Connecting an agent to TikTok Ads for the first time | `references/01-mcp-server.md` | official MCP: no app, no API key, browser OAuth |
| About to create anything through MCP | `references/00-launch-runbook.md` § 5–7 | `ttops preflight` → MCP → `ttops audit`; both offline |
| Handed a token / asked to launch via API | `references/00-launch-runbook.md` | create a workspace in a project dir, `ttops doctor`, then follow the runbook |
| In a directory with `workspace.json` | `references/04-ttops-cli.md` | `ttops --json doctor` before anything else |
| Hit an error code, a gate, a rejection | `references/05-error-catalog.md` or `ttops explain <code>` | the file it names |
| Asked "can the API even do X" | `references/01` § tool surface, then `references/03` | |
## The four facts that cost money if you don't know them
1. **`operation_status` defaults to `ENABLE`.** Omit it and your campaign is created **live and
spending**. This is the opposite of Meta. Pass `DISABLE` at campaign, ad group *and* ad, every
time. `ttops` forces it and offers no override.
2. **A successful create is not proof the object holds what you sent.** TikTok drops unknown keys and
fills enum defaults silently. Read every object back and diff it before activating.
3. **Budget minimums are `base × per-currency ratio`, and nine currencies take no decimals.** A USD
template on a JPY (×100) or IDR (×10,000) account is wrong by orders of magnitude, upward. Run
`ttops budget-check --currency X` before writing a spec.
4. **Throttling arrives in the response body, as HTTP 200 with `code` 40100/40133/40016.** Branch on
the body code, never on HTTP status — TikTok is not documented to send 429 and handling keyed on
status will loop forever without noticing. If a 429 ever does arrive, treat it as throttling too;
the rule is "always read the body code", not "429 cannot happen".
Plus one that costs a day: **Marketing API advertiser tokens never expire.** So error 40102 "token
expired" on a Marketing API call is a strong signal that you are holding a TikTok-*account* token
(1 day) rather than an advertiser token — the two are conflated constantly, including in TikTok's
own SDK docs. Check the token's origin before concluding it; it is the first hypothesis, not a
certainty.
## MCP is the default. `ttops` is the guard rail.
TikTok ships an **official MCP server** (July 2026) that covers essentially the whole job — campaign
creation, budgets, targeting, creative upload, catalogs, audiences, reporting, automated rules,
webhooks, Business Center operations. ~400 tools. **No developer app, no API key**: the operator
authorizes once in a browser.
```bash
claude mcp add --transport http tiktok-ads \
https://business-api.tiktok.com/open_mcp/tt-ads-mcp-flat
```
**Use MCP for the work.** It is not a fallback and not read-only.
What it does not give you is *guarantees* — it is a faithful wrapper over endpoints whose defaults
are dangerous. That is what `ttops` supplies, and **both of its guard commands run offline with no
token**, which matters because an operator who cannot register a developer app has MCP and nothing
else:
```bash
ttops --json preflight --spec specs/mine.json --currency JPY # before the MCP calls
ttops --json audit --spec specs/mine.json --actual readback.json --currency JPY # after them
```
| Gap in MCP | What `ttops` does |
|---|---|
| `operation_status` defaults to **ENABLE** — omit it and the campaign spends | `preflight` emits every create with `DISABLE` |
| No budget validation; 50 on a JPY account is 100× wrong | `preflight`, `budget-check` |
| `code: 0` ≠ the object holds what you sent | `audit` diffs the read-back and flags anything live |
| `AD_STATUS_AUDIT_DENY` (rejected) reads like `AD_STATUS_AUDIT` (in review) | `explain <status>` |
| No enum validation — a `SALES` objective fails at the call | `preflight` |
**Reach for the direct API only in three cases:** bulk across many accounts, unattended/scheduled runs
(MCP's authorization is browser-bound and expires in 30 days; an advertiser token does not), or an
operation MCP does not wrap. Then the full `ttops` lifecycle below applies.
## Lifecycle
**Through MCP** (the normal case) — full detail in `references/00-launch-runbook.md`:
```
doctor-by-hand (auth_advertiser_get, advertiser_info_get, pixel_list_get)
→ ttops preflight validate + emit exact tool arguments
→ MCP creates, all DISABLED campaign_create → adgroup_create → ad_create
→ read back campaign_get / adgroup_get / ad_get → readback.json
→ ttops audit diff + danger flags
→ human review while paused preview, destination, review status, balance
→ enable bottom-up ad → adgroup → campaign
```
**Through the API** (bulk / unattended), receipt-gated at every step:
```bash
export TIKTOK_ACCESS_TOKEN='...' # never on argv
cd ~/work/tiktok-<account> # project dir, NOT the skill dir
ttops --json doctor
ttops --json plan --spec specs/mine.json
ttops --json apply --plan .ttops/plans/<plan>.json
ttops --json verify --plan .ttops/plans/<plan>.json
ttops --json activate --plan .ttops/plans/<plan>.json \
--confirm-reviewed REVIEWED --confirm SPEND
```
`doctor` receipts expire after 24h, `verify` after 1h — an hour-old read is not evidence about what
is live now.
Run without installing: `PYTHONPATH=<skill>/scripts python3 -m ttops …` (stdlib only, no deps).
## Non-negotiables
1. **Nothing is created enabled.** Creation and activation are separate commands, and activation
carries an explicit `--confirm SPEND`.
2. **Verify before activate, always.** A create returning `code: 0` proves the call was accepted, not
that the object is what you designed.
3. **Never hand-assemble a payload from memory.** The agent writes a spec; `ttops preflight`
turns it into the arguments — which you then hand to the MCP tool, or which `ttops apply` posts
directly on the token path. Either way the arguments come from validated normalisation, not from
improvising JSON at the call site. A new campaign shape means extending `spec.py` and its tests.
4. **A dropped connection on a write is an UNKNOWN outcome, not a failure.** Never blind-retry a
create. Reconcile against Ads Manager first; that is how duplicates happen.
5. **The token is a bearer secret.** Not in chat, URLs, screenshots, repos, logs, or argv.
6. **Any budget or billing anomaly: pause first, diagnose second.** Debating units while a campaign
spends is how a units error becomes an incident.
7. **The agent cannot fund an account.** Balance top-ups, payment methods and refunds are UI-only and
Finance-role-gated. Funding is an escalation to a human, never a blocked automation.
8. **Access problems are human problems.** 40125 is a missing *app scope* (a developer fixes it);
40001 is an insufficient *role on the asset* (a BC Admin fixes it). Retrying fixes neither.
## References
| Need | File |
|---|---|
| **Official MCP server — the default surface**: URLs, setup, 30-day authorization, full tool surface, what it does not guarantee | `references/01-mcp-server.md` |
| **Ordered launch path (MCP-first) — start here for any launch** | `references/00-launch-runbook.md` |
| Developer app gate, token types and expiry, scopes, rate limits, sandbox, SDK limits, handoff checklist | `references/02-api-access-and-tokens.md` |
| Enums, create payloads, CBO, budget/currency math, batch limits, idempotency, deprecations | `references/03-api-enums-and-payloads.md` |
| **`ttops` command contract**: workspace, lifecycle, receipts, failure contract, extending it | `references/04-ttops-cli.md` |
| Return codes: cause → fix, triage order, and the errors that return `code: 0` | `references/05-error-catalog.md` |
| Reporting at scale, automated rules via API, webhooks, audience limits, split tests, no-code layer | `references/06-monitoring-rules-webhooks.md` |
| Strategy, objectives, targeting, creative, policy, tracking, diagnosis | skill `tiktok-ads` |
Scripts live in `scripts/ttops/`; example workspace and specs in `specs/`. Offline suite:
`PYTHONPATH=scripts python3 -m ttops.selftest` (102 tests) — green before shipping any change.
## Boundary
Owns access, payload correctness, create→verify→activate, and error response. Owns **no opinion
about what to launch**: objective, optimization event, structure, targeting, budgets and creative
come from `tiktok-ads`. Picking an objective here means a step was skipped.
For regulated verticals the authorization gate is cleared **before any ad object exists** and binds
to a specific verified entity and ad account (`tiktok-ads/08`). This skill does not route around ad
review and does not implement cloaking or review-layer filtering.