@bluevs/ttcli 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/skills/ttcli/SKILL.md +254 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluevs/ttcli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "TikTok CLI - designed for AI Agent and developers",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ttcli": "scripts/run.js"
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"postinstall": "node -e \"console.log('\\n📦 ttcli installed. To install the openclaw skill, run:\\n bash $(npm root -g)/@bluevs/ttcli/skills/ttcli/scripts/install-skill.sh\\n')\""
|
|
10
10
|
},
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"@bluevs/ttcli-darwin-arm64": "0.0.
|
|
13
|
-
"@bluevs/ttcli-darwin-x64": "0.0.
|
|
14
|
-
"@bluevs/ttcli-linux-arm64": "0.0.
|
|
15
|
-
"@bluevs/ttcli-linux-x64": "0.0.
|
|
16
|
-
"@bluevs/ttcli-win32-arm64": "0.0.
|
|
17
|
-
"@bluevs/ttcli-win32-x64": "0.0.
|
|
12
|
+
"@bluevs/ttcli-darwin-arm64": "0.0.8",
|
|
13
|
+
"@bluevs/ttcli-darwin-x64": "0.0.8",
|
|
14
|
+
"@bluevs/ttcli-linux-arm64": "0.0.8",
|
|
15
|
+
"@bluevs/ttcli-linux-x64": "0.0.8",
|
|
16
|
+
"@bluevs/ttcli-win32-arm64": "0.0.8",
|
|
17
|
+
"@bluevs/ttcli-win32-x64": "0.0.8"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=16"
|
package/skills/ttcli/SKILL.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ttcli
|
|
3
|
-
version: 0.0.
|
|
3
|
+
version: 0.0.8
|
|
4
4
|
description: 直接调用 TikTok Marketing API(access token 鉴权,不经 VH 平台/项目层)。当用户提供原生 TikTok access_token + advertiser_id、要直连 TikTok 创建或查询 Smart+ campaign/adgroup/ad、或查 identity/app/region 等元数据时使用。注意:用户走 VH 项目流程(po_xxxx + account_id)的 TikTok 创编应改走 vhcli tiktok。
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# TikTok CLI (`ttcli`)
|
|
8
8
|
|
|
9
|
-
> **Skill version**: `0.0.
|
|
9
|
+
> **Skill version**: `0.0.8`
|
|
10
10
|
>
|
|
11
11
|
> 当用户问"ttcli skill 版本是多少 / skill 装好了吗 / 升级生效了没" 时,**直接念出上面的版本号**。
|
|
12
12
|
> 用户期望更高版本但没看到 → 让他们:(1) 重跑 `npm install -g @bluevs/ttcli && bash $(npm root -g)/@bluevs/ttcli/skills/ttcli/scripts/install-skill.sh` (2) 然后**新开一个会话**(skill 在会话启动时加载,老会话不刷新)。
|
|
@@ -33,6 +33,105 @@ This skill is intentionally short. Always treat `ttcli -h` and `ttcli <subcomman
|
|
|
33
33
|
|
|
34
34
|
如果用户问「能不能直接调 X 接口」,先回「我们走 ttcli,让我看看包了没;没包的话我把这个接口加到 ttcli 里再用,会更稳」。
|
|
35
35
|
|
|
36
|
+
## 🚨 硬性约定:Token 走 vhcli bridge,禁止找用户直接要 token
|
|
37
|
+
|
|
38
|
+
**ttcli 不自己持有 token**,靠 vhcli(Vision Hub CLI)发证。用户**永远**不需要手填 access_token——你看到 ttcli 报缺 token 时,自动走下面这段流程,不要把 token 这种技术词汇暴露给用户。
|
|
39
|
+
|
|
40
|
+
### 协议触发条件
|
|
41
|
+
|
|
42
|
+
ttcli 在 token 缺失时返回的错误**首行包含 `TTCLI_TOKEN_MISSING`** marker。看到这串就触发 bridge,**不要把这条原文展示给用户**——它是机器协议,不是用户对话。
|
|
43
|
+
|
|
44
|
+
### Bridge 步骤
|
|
45
|
+
|
|
46
|
+
1. **拿 customer_project_id**:
|
|
47
|
+
- 用户对话里已经提到的项目("VH 项目 PO_xxxx"、"客户项目 ID 12345"等)→ 优先用
|
|
48
|
+
- 没提到 → `vhcli project list` 自己捞,然后跟用户确认是哪个项目(**用项目名字而不是 ID**:「你这次想用哪个项目,A / B / C?」)
|
|
49
|
+
- 用户没绑过任何项目 → 让用户去 VH 后台先绑,然后回来再说
|
|
50
|
+
|
|
51
|
+
2. **拿 advertiser_id(TikTok 广告账户 ID)**:
|
|
52
|
+
- 用户对话里已经给过 / ttcli 的 `--advertiser-id` 已经设了 → 用那个
|
|
53
|
+
- 没有 → 从 vhcli 列出的项目下属账户里挑(**用账户名而不是 ID**),跟用户确认
|
|
54
|
+
|
|
55
|
+
3. **调 vhcli 拿 token**:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
vhcli project account-token \
|
|
59
|
+
--customer-project-id=<NUM> \
|
|
60
|
+
--account-id=<advertiser_id> \
|
|
61
|
+
--format=json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
vhcli 会**同时**:
|
|
65
|
+
- stdout 输出 JSON,含 `access_token` 字段
|
|
66
|
+
- 落到 `~/.vhcli/ad_tokens.json`
|
|
67
|
+
|
|
68
|
+
4. **把 token 喂给 ttcli**——选其一:
|
|
69
|
+
|
|
70
|
+
**临时(推荐)**:从 vhcli stdout JSON 抽 `access_token`,紧接着的 ttcli 调用加 `--token=<TOKEN>`:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
TOKEN=$(vhcli project account-token --customer-project-id=N --account-id=A --format=json | jq -r .access_token)
|
|
74
|
+
ttcli --token="$TOKEN" --advertiser-id=A bc list ...
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**持久化(用户长期就这一个账号、不切换时)**:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ttcli auth set --token="$TOKEN" --advertiser-id=A
|
|
81
|
+
# 之后所有 ttcli 调用都不用再传 --token
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
5. **重跑**用户最初的 ttcli 命令。
|
|
85
|
+
|
|
86
|
+
### 跟用户怎么说
|
|
87
|
+
|
|
88
|
+
用户**完全不应该**看到 `TTCLI_TOKEN_MISSING`、access_token、customer_project_id 这些词。整个 bridge 在你脑子里悄悄跑。需要用户输入时只问业务问题,比如:
|
|
89
|
+
- ❌ "ttcli 报 TTCLI_TOKEN_MISSING,请提供 customer_project_id 和 advertiser_id"
|
|
90
|
+
- ✅ "我先确认一下:你这次是用哪个项目下的哪个广告账户?"
|
|
91
|
+
|
|
92
|
+
token 拿到之后也不要回报"我已经从 vhcli 取了 token"——直接做用户要的事。
|
|
93
|
+
|
|
94
|
+
### 失败模式
|
|
95
|
+
|
|
96
|
+
- vhcli 报"账户没授权"/"未绑定项目" → 翻译给用户:「这个广告账户在 VH 这边还没绑到这个项目下,麻烦先在 VH 后台关联一下」
|
|
97
|
+
- vhcli 找不到(没装 / PATH 没配)→ 这是部署问题,跟用户说「我这边 vhcli 跑不起来,请联系部署的同事看看」
|
|
98
|
+
- vhcli 拿到的 token ttcli 一调就 `code=40105 token expired/revoked` → 重跑 vhcli 那一步,token 可能 vhcli 本地缓存过期
|
|
99
|
+
|
|
100
|
+
## 🔍 如何发现 ttcli 能力(不要靠记忆)
|
|
101
|
+
|
|
102
|
+
SKILL.md **不**枚举所有 ttcli 子命令;项目还在长。新会话或不确定 ttcli 是否已支持某能力时,走两步发现:
|
|
103
|
+
|
|
104
|
+
**第 1 步 — `ttcli schema`(默认 JSON)一次拿全树**
|
|
105
|
+
- 输出含每条命令的 `path` / `short` / `long` / `flags`(含 `name`/`type`/`default`/`usage`/`required`)
|
|
106
|
+
- AI agent 视角的"能力清单 API":一条命令拉到全部,免去靠记忆
|
|
107
|
+
- 速看人类视图:`ttcli --human schema --depth=2`(root + namespace + 子命令短描述)
|
|
108
|
+
|
|
109
|
+
**第 2 步 — `ttcli <path> -h`(cobra 自带)看具体细节**
|
|
110
|
+
- 完整 long help(含示例段落、关键链路提示,如"返回的 X ID 喂入 adgroup body 的 Y 字段")
|
|
111
|
+
- 每个 flag 的默认值、required 标记、shorthand
|
|
112
|
+
|
|
113
|
+
**典型用法**(jq 配合 schema JSON):
|
|
114
|
+
```bash
|
|
115
|
+
# 找名字含 hashtag 的所有命令
|
|
116
|
+
ttcli schema | jq '.. | objects | select(.short | test("hashtag"; "i")) | .path'
|
|
117
|
+
|
|
118
|
+
# 看 tool namespace 下所有子命令
|
|
119
|
+
ttcli schema | jq '.. | objects | select(.path == "ttcli tool") | .subcommands[].path'
|
|
120
|
+
|
|
121
|
+
# 看某命令的所有 required flag
|
|
122
|
+
ttcli schema | jq '.. | objects | select(.path == "ttcli identity video list") | .flags[] | select(.required) | .name'
|
|
123
|
+
|
|
124
|
+
# 找所有支持 --dry-run 的写命令
|
|
125
|
+
ttcli schema | jq '.. | objects | select(.flags // [] | map(.name) | index("dry-run")) | .path'
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**何时用**:
|
|
129
|
+
- 不确定 ttcli 能不能做某事 → 第 1 步(grep / jq 过滤)
|
|
130
|
+
- 选定命令、要写正确 flag 串 → 第 2 步
|
|
131
|
+
- 这两步替代"靠记忆猜命令" + "硬上 curl 直调"(hard rule #1 禁止)
|
|
132
|
+
|
|
133
|
+
**注意**:cobra 层 required 跟业务必填不一定一致——很多 create 类命令 cobra flag 不标 required,但 `Validate()` / TikTok 服务端会拒。`long` 文本里的 `REQUIRED:` 段落 + 实际报错才是终极真理;schema 的 `required` 字段是辅助信号。
|
|
134
|
+
|
|
36
135
|
## 🚨 与用户对话的硬性约定:错误信息要翻译
|
|
37
136
|
|
|
38
137
|
ttcli 和 TikTok 返回的报错都是技术语言(`missing required fields: [adgroup_id]`、`Invalid optimization goal`、`code=40002 ...`)。**严禁原样把这些贴给用户**——非技术用户看到字段名、code 数字会懵。
|
|
@@ -61,18 +160,59 @@ ttcli 和 TikTok 返回的报错都是技术语言(`missing required fields: [
|
|
|
61
160
|
- 多个字段同时缺 → 合并成一个自然问句,不要逐个列字段名
|
|
62
161
|
- request_id 留你脑子里 / 日志里,**不主动甩给用户**,除非用户明确要"找 TikTok 提工单"
|
|
63
162
|
|
|
64
|
-
##
|
|
163
|
+
## 🚨 与用户对话的硬性约定:能力清单也要翻译
|
|
164
|
+
|
|
165
|
+
跟用户聊"你能做什么"的时候,**严禁直接吐 CLI 命令名 / namespace / 技术术语**。用户问"你有哪些财务能力",不要回:
|
|
166
|
+
|
|
167
|
+
> ❌ "ttcli advertiser 模块有 balance / transaction / budget;ttcli bc 模块有 cost / transfer / invoice / billing-group / payment-portfolio / changelog-task ..."
|
|
65
168
|
|
|
66
|
-
|
|
169
|
+
这是把内部命令树原样念给用户。非技术用户看不懂 namespace 划分、不关心 CLI 路径、不知道 BC / Portfolio / 异步任务是什么。
|
|
67
170
|
|
|
68
|
-
|
|
171
|
+
**正确做法**:把能力翻译成**用户视角的事情**——"你想干嘛 → 我能帮你做",不是"我有哪些命令"。
|
|
172
|
+
|
|
173
|
+
参考翻译表:
|
|
174
|
+
|
|
175
|
+
| 技术能力名(你看到的) | 翻给用户(你要说的) |
|
|
69
176
|
|---|---|
|
|
70
|
-
|
|
|
71
|
-
| `
|
|
72
|
-
|
|
|
73
|
-
|
|
|
177
|
+
| `ttcli advertiser balance` / `bc balance` | "查你的广告账户里还剩多少钱" |
|
|
178
|
+
| `ttcli bc cost` | "查最近广告实际花了多少(注意有约 10 小时延迟)" |
|
|
179
|
+
| `ttcli advertiser transaction` / `bc transaction` | "看充值、扣款、退款的流水明细" |
|
|
180
|
+
| `ttcli bc transfer` | "把钱从商务中心调到广告账户、或反向调回" |
|
|
181
|
+
| `ttcli bc invoice` | "查发票、下载发票" |
|
|
182
|
+
| `ttcli bc billing-group` | "管理账单分组(哪些广告账户合并成一张账单)" |
|
|
183
|
+
| `ttcli bc payment-portfolio` | "管理财务资产组合(多账户共享一个钱袋的高级功能)" |
|
|
184
|
+
| `ttcli advertiser budget` | "查广告主预算和它的变更历史" |
|
|
185
|
+
| `ttcli bc changelog-task` | "导出广告账户的修改历史" — 用户说"我想看谁改了什么"就够了,**不要提"异步任务"**|
|
|
186
|
+
|
|
187
|
+
**原则**:
|
|
188
|
+
- 用户视角组织,不是 namespace 视角。"管钱"是一类,不是"advertiser 一类 + bc 一类"
|
|
189
|
+
- 不出现 CLI 命令字面量、不出现 `ttcli` / `--help` / `namespace` / `endpoint`、不出现"白名单功能 / 异步任务 / changelog"等纯实现术语
|
|
190
|
+
- 用动词起头:"查"、"导"、"调钱"、"看明细",让用户立刻判断"我要不要做这个"
|
|
191
|
+
- 末尾邀请用户给具体诉求:"你具体想看哪个?",**不要**列 `ttcli xxx --help` 让用户自己研究
|
|
192
|
+
- 唯一例外:用户**明确说自己是开发者 / 想看命令 / 想自己跑**,那时再列 CLI 命令
|
|
193
|
+
|
|
194
|
+
## ttcli vs vhcli — 它们是什么关系
|
|
195
|
+
|
|
196
|
+
**两个工具不是 either/or 替代品**,是上下游:
|
|
197
|
+
|
|
198
|
+
```
|
|
199
|
+
vhcli (project / token / audit)
|
|
200
|
+
│
|
|
201
|
+
│ 发 access_token
|
|
202
|
+
▼
|
|
203
|
+
ttcli (TikTok Marketing API consumer)
|
|
204
|
+
│
|
|
205
|
+
│ Access-Token header
|
|
206
|
+
▼
|
|
207
|
+
business-api.tiktok.com
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
| 工具 | 干什么 | 何时用 |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| **vhcli** | VH 平台项目管理:列项目、列账户、**发 access_token**、项目级审计/审批 | (1) token bridge 的发证方(见上面"Token 走 vhcli bridge"协议)(2) 用户问"项目 PO_xxxx 下有哪些账户"这种 VH 元数据问题 |
|
|
213
|
+
| **ttcli** | TikTok Marketing API 的瘦客户端:~700 个 cobra 命令对应官方所有 endpoint | 一切实际 TikTok 业务操作(创广告、查余额、列 campaign、看流水……) |
|
|
74
214
|
|
|
75
|
-
|
|
215
|
+
**判断准则**:操作目标是 TikTok 实体(campaign/adgroup/ad/identity/...)→ ttcli。操作目标是 VH 元数据(project/team/audit)→ vhcli。token 永远走 vhcli 发,永远不让用户手填。
|
|
76
216
|
|
|
77
217
|
|
|
78
218
|
## Installation
|
|
@@ -120,7 +260,21 @@ Before creating a campaign or adgroup, gather the IDs you'll reference:
|
|
|
120
260
|
|
|
121
261
|
```
|
|
122
262
|
ttcli auth show # confirm credentials loaded + their source
|
|
263
|
+
ttcli user info # → who am I? (TikTok for Business user behind the access token)
|
|
123
264
|
ttcli bc list # → bc_id (Business Centers; needed for BC_AUTH_TT identities)
|
|
265
|
+
ttcli bc changelog list --bc-id=B # BC audit log (member/account/asset/settings changes)
|
|
266
|
+
ttcli bc balance --bc-id=B # BC's own wallet balance (requires finance role)
|
|
267
|
+
ttcli bc transaction list --bc-id=B # transaction history (BC / advertiser / portfolio level, 13 transaction_type)
|
|
268
|
+
ttcli bc transaction summary --bc-id=B # BC aggregated by date × funds_type × invoice + top-level totals
|
|
269
|
+
ttcli bc cost list --bc-id=B # actual ad spend per advertiser (NEGATIVE amounts; ~10h delay)
|
|
270
|
+
ttcli bc payment-portfolio list --bc-id=B # financial portfolios (whitelist feature; SHARED/NON_SHARED)
|
|
271
|
+
ttcli bc payment-portfolio create --name=X --advertiser-ids=A,B # create SHARED portfolio (max 20 advertisers)
|
|
272
|
+
ttcli bc payment-portfolio attach --portfolio-id=P --advertiser-ids=A,B # attach more advertisers to existing SHARED portfolio
|
|
273
|
+
ttcli bc payment-portfolio advertiser list --portfolio-id=P # advertisers currently attached to a portfolio
|
|
274
|
+
ttcli bc payment-portfolio credit-line update --portfolio-ids=P1,P2 --percentages=0.6,0.4 # allocate credit by % across SHARED portfolios
|
|
275
|
+
ttcli advertiser balance --bc-id=B # advertiser balances under a BC (requires finance role)
|
|
276
|
+
ttcli advertiser transaction list --bc-id=B # legacy advertiser transactions (CASH/GRANT split, 4 transfer_type)
|
|
277
|
+
ttcli advertiser budget changelog list --bc-id=B --advertiser-id=A # budget modification audit (who changed what budget when)
|
|
124
278
|
ttcli app list # → app_id (Android/iOS apps)
|
|
125
279
|
ttcli minis list # → minis_id (TikTok Minis: MINI_GAME / MINI_SERIES)
|
|
126
280
|
ttcli identity list # → identity_id (Spark Ads publishing identity)
|
|
@@ -139,8 +293,53 @@ ttcli tool targeting info \
|
|
|
139
293
|
--objective-type=APP_PROMOTION --promotion-type=APP_ANDROID \
|
|
140
294
|
--placements=PLACEMENT_TIKTOK \
|
|
141
295
|
--targeting-ids=6254925,6254928 # ← reverse: name + parent hierarchy from IDs (use --scene=ISP for isp_id lookup)
|
|
296
|
+
ttcli tool language # → language codes for targeting_spec.languages (audience targeting; not the same as --language=zh-CN on tool region which is *display* language)
|
|
297
|
+
ttcli tool interest-category list # → general interest tree (lighter than 'targeting search'; only needs --advertiser-id)
|
|
298
|
+
ttcli tool interest-keyword recommend # → other-interest keyword IDs from a seed (single OR multi keyword)
|
|
299
|
+
ttcli tool interest-keyword info # ← reverse: keyword_id → name + EFFECTIVE/INEFFECTIVE status
|
|
300
|
+
ttcli tool action-category list # → behavior categories (CREATOR_RELATED + VIDEO_RELATED in one call)
|
|
301
|
+
ttcli tool hashtag recommend # → hashtag IDs from seeds (action_scene=HASHTAG_RELATED)
|
|
302
|
+
ttcli tool hashtag info # ← reverse: keyword_id → hashtag + ONLINE/OFFLINE
|
|
303
|
+
ttcli tool targeting-category recommend # → system-recommended interest+action subset for region/app
|
|
304
|
+
ttcli tool search-keyword recommend # → Search-Ads keywords + monthly search volume (US only)
|
|
305
|
+
ttcli tool search-keyword idea # → keyword research (volume RANGE + 3mo/yoy trend + competition + CPC)
|
|
306
|
+
ttcli tool diagnosis search-health # → Search Ads adgroup/ad health (volume + relevance + bid/budget)
|
|
307
|
+
ttcli tool os-version list # → audience OS version IDs (per --os-type=ANDROID|IOS; os_id NOT globally unique)
|
|
308
|
+
ttcli tool device-model list # → device model tree (3-level BRAND/SERIES/MODEL; ~14k total but only ~12% is_active)
|
|
309
|
+
ttcli tool carrier list # → mobile carriers per country (~69 countries / ~251 carriers, ~92% in_use)
|
|
310
|
+
ttcli tool targeting list # → ISP (broadband) targeting tags by country (NOT mobile carriers; feeds isp_ids)
|
|
311
|
+
ttcli tool contextual-tag list # → contextual targeting tags (whitelist; REACH/VIDEO_VIEWS/RF_REACH only)
|
|
312
|
+
ttcli tool contextual-tag info # ← reverse: contextual_tag_id → details. NOTE: list can return empty while info still works (use info for whitelist validation)
|
|
313
|
+
ttcli tool content-exclusion list # → content exclusion + vertical sensitivity categories (REACH/VIDEO_VIEWS/ENGAGEMENT only)
|
|
314
|
+
ttcli tool content-exclusion info # ← reverse: category_id → details (mix EXCLUSION + VERTICAL OK; merged into one list)
|
|
315
|
+
ttcli tool bid recommend # → TikTok-suggested bid for cost-cap (BID_TYPE_CUSTOM); TRAFFIC/WEB_CONVERSIONS only
|
|
316
|
+
ttcli tool vbo-status # → check Value-Based Optimization support for adgroup config (12 status fields)
|
|
317
|
+
ttcli tool brand-safety partner-status # → 3rd-party brand-safety partner authz (Zefr only)
|
|
318
|
+
ttcli tool url-validate # → check if a URL is a valid Universal Link / App Link / custom scheme
|
|
319
|
+
ttcli tool phone-region-code # → 235 phone region entries (ISO + calling code, e.g. US/+1, AT/+43)
|
|
320
|
+
ttcli tool timezone # → 150 timezones; gmt_offset is FIXED standard-time (ignores DST)
|
|
321
|
+
ttcli tool open-url # → TikTok web URL → in-app sslocal:// deep link (for RF Reach ads' deeplink field)
|
|
322
|
+
ttcli targeting search # → interest/behavior/hashtag IDs (NOT 'tool targeting'; see note below)
|
|
323
|
+
ttcli campaign-label list # → list campaign labels (tags applied to campaigns; GENERAL / MARKETING_EVENT)
|
|
324
|
+
ttcli creative auto-message create # → create welcome message (DM Ads; auto-reply Q&A library)
|
|
325
|
+
ttcli creative auto-message list # ← list welcome messages (audit_status: AUDITING/PASS/REJECTED)
|
|
142
326
|
```
|
|
143
327
|
|
|
328
|
+
**⚠️ Two `targeting search` endpoints, do not conflate:**
|
|
329
|
+
|
|
330
|
+
| Command | URL | Returns | Feeds into |
|
|
331
|
+
|---|---|---|---|
|
|
332
|
+
| `ttcli tool targeting search` | `/tool/targeting/search/` | GEO IDs / ZIP IDs from name | `targeting_spec.location_ids` / `zipcode_ids` |
|
|
333
|
+
| `ttcli targeting search` | `/targeting/search/` (no `tool/` prefix!) | Interest / behavior / hashtag IDs | `targeting_spec.interest_category_ids` / `interest_keyword_ids` / `purchase_intention_keyword_ids` / `actions[].action_category_ids` |
|
|
334
|
+
| `ttcli tool interest-category list` | `/tool/interest_category/` | The full general-interest tree (no keyword search) | `targeting_spec.interest_category_ids` (overlaps with `targeting search` GENERAL_INTEREST browse mode; pick whichever; this one only needs `--advertiser-id`) |
|
|
335
|
+
| `ttcli tool interest-keyword recommend` | `/tool/interest_keyword/recommend/` | "Other-interest" keyword IDs from a seed (status=EFFECTIVE only) | `targeting_spec.interest_keyword_ids` (overlaps with `targeting search --keywords=...` ADDITIONAL_INTEREST; this endpoint is the simpler one if all you need is interest_keyword reverse lookup) |
|
|
336
|
+
| `ttcli tool action-category list` | `/tool/action_category/` | Behavior categories tagged by `action_scene` (CREATOR_RELATED + VIDEO_RELATED) | adgroup body `actions[]`: `{action_scene, action_category_ids, action_period}` — group IDs by scene (each `actions[]` entry uses ONE scene). action_category_id is NOT globally unique; same id="10" exists in both scenes. |
|
|
337
|
+
| `ttcli tool hashtag recommend` | `/tool/hashtag/recommend/` | Hashtag IDs from seed keywords (status=ONLINE only) | adgroup body `actions[]` with `action_scene=HASHTAG_RELATED`. **Same field name `action_category_ids` reused** — distinguish by `action_scene`. Operator AND combines all keywords; OR returns per-keyword (with `input_keyword`). |
|
|
338
|
+
| `ttcli tool targeting-category recommend` | `/tool/targeting_category/recommend/` (POST) | Recommended SUBSET for region+app (industry-driven) — interest + action only (no hashtag) | `targeting_spec.interest_category_ids` AND `actions[].action_category_ids` (with `action_scene` from response). Use this when you want a pre-curated starting set instead of the full tree. |
|
|
339
|
+
| `ttcli tool search-keyword recommend` | `/tool/search_keyword/recommend/` | Search-Ads keyword recommendations + estimated monthly search volume | Search Ads campaigns (NOT feed/Smart+ targeting). Three input modes (mutually exclusive): `--search-queries` (max 5 English words) / `--ad-ids` (whitelist) / `--landing-page-urls` (whitelist). Regions: US only. |
|
|
340
|
+
| `ttcli tool search-keyword idea` | `/tool/search_keyword/keyword_idea/` | Keyword research — volume RANGE + 3mo/yoy trend + competition + CPC range | Search Ads campaigns. Use when you need bid/budget planning data, not just expansion. Up to 1 country (12 supported, US default). `--brand-type` only for US. |
|
|
341
|
+
| `ttcli tool diagnosis search-health` | `/tool/diagnosis/search/health/` | Health diagnosis for a Search-Ads adgroup or ads (search_volume / keyword_relevance / bid_budget) | Search Ads only. `--adgroup-id` OR `--ad-ids` (max 50, all in same adgroup; both passed → adgroup_id ignored server-side). Returns suggested bid + relevance breakdown per ad. |
|
|
342
|
+
|
|
144
343
|
`tool region` accepts `--language=zh-CN` (or any of 78 supported codes) for localized region names — useful when surfacing options to the user.
|
|
145
344
|
|
|
146
345
|
## Write safety: ALWAYS dry-run first
|
|
@@ -344,7 +543,12 @@ Per the doc: "对于现有广告账号,当广告组的投放广告位为自动
|
|
|
344
543
|
| `tiktok_item_id` (Spark Ads) | `/tt_video/info/` | `ttcli tt-video info --auth-code=...` |
|
|
345
544
|
| List posts under an identity | `/identity/video/get/` | `ttcli identity video list --identity-id=... --identity-type=...` |
|
|
346
545
|
| Verify `identity_id` | `/identity/info/` | `ttcli identity info --identity-id=... --identity-type=...` |
|
|
546
|
+
| List native series under an identity | `/identity/native_series/get/` | `ttcli identity native-series list --identity-id=... --identity-type=...` |
|
|
347
547
|
| Search ad-library videos (`video_id`) | `/file/video/ad/search/` | `ttcli file video search` |
|
|
548
|
+
| Upload video to ad library | `/file/video/ad/upload/` | `ttcli file video upload --video-file=PATH` (or `--upload-type=UPLOAD_BY_URL --video-url=...`) |
|
|
549
|
+
| Inspect ad-library video by ID | `/file/video/ad/info/` | `ttcli file video info --video-ids=...` (max 60; unusable IDs silently dropped) |
|
|
550
|
+
| Suggest cover images for a video | `/file/video/suggestcover/` | `ttcli file video suggest-cover --video-id=...` (wait 30s-5min after upload for transcoding) |
|
|
551
|
+
| Rename an ad-library video | `/file/video/ad/update/` | `ttcli file video update --video-id=... --file-name=...` (collisions auto-suffixed by TikTok) |
|
|
348
552
|
| Search ad-library images (`image_id`) | `/file/image/ad/search/` | `ttcli file image search` |
|
|
349
553
|
| Upload video → `video_id` | `/file/video/ad/upload/` | ⏳ not yet wrapped |
|
|
350
554
|
| Upload image → `web_uri` | `/file/image/ad/upload/` | ⏳ not yet wrapped |
|
|
@@ -380,6 +584,42 @@ For MINI_GAME or any TikTok-placement ad creation:
|
|
|
380
584
|
|
|
381
585
|
Note: TikTok identity-list pagination requires `identity_type` to be set — without a type filter, `page_info` is null in the response.
|
|
382
586
|
|
|
587
|
+
## Automated Rules (`optimizer rule create`)
|
|
588
|
+
|
|
589
|
+
Set conditions on campaigns/adgroups/ads (CPA > X, CTR < Y, etc.) and trigger actions automatically (turn on/off, adjust budget/bid, message). Like adgroup create, the body is deeply nested (`apply_objects[]`, `conditions[]`, `actions[]` with nested `value`/`frequency_info`, `notification`, `rule_exec_info` with optional `time_period_info[]`) so it's a body-driven endpoint:
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
ttcli optimizer rule create --body-from-file=/tmp/rule.json --dry-run # preview create
|
|
593
|
+
ttcli optimizer rule create --body-from-file=/tmp/rule.json # create
|
|
594
|
+
ttcli optimizer rule update --body-from-file=/tmp/rule.json --dry-run # preview update (must include rule_id per rule)
|
|
595
|
+
ttcli optimizer rule update --body-from-file=/tmp/rule.json # update
|
|
596
|
+
ttcli optimizer rule status update --operate-type=TURN_ON --rule-ids=R # enable / disable / delete in bulk
|
|
597
|
+
ttcli optimizer rule status update --operate-type=DELETE --rule-ids=R --dry-run
|
|
598
|
+
ttcli optimizer rule batch-bind --rule-id=R --dimension=CAMPAIGN \
|
|
599
|
+
--dimension-ids=C1,C2 --bind-type=BIND # attach rule to objects
|
|
600
|
+
ttcli optimizer rule batch-bind --rule-id=R --dimension=CAMPAIGN \
|
|
601
|
+
--dimension-ids=C1 --bind-type=UNBIND --dry-run # detach (preview)
|
|
602
|
+
ttcli optimizer rule info --rule-ids=6984697088064290818 # by IDs (read-after-write)
|
|
603
|
+
ttcli optimizer rule list --status=ON # by filters/pagination
|
|
604
|
+
ttcli optimizer rule list --data-dimension=CAMPAIGN --page=1 --page-size=20
|
|
605
|
+
ttcli optimizer rule result list --action=MESSAGE # execution history (aggregate)
|
|
606
|
+
ttcli optimizer rule result list --time='2026-06-01 00:00:00,2026-06-30 23:59:59'
|
|
607
|
+
ttcli optimizer rule result info --rule-ids=R1 --exec-ids=E1 # per-object outcomes
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
- `create`, `update`, `status update`, and `batch-bind` are write endpoints — all support `--dry-run`. `create`/`update` return `rule_ids[]`; `status update` returns the rule_ids that were successfully changed; `batch-bind` returns empty `data:{}` (success means everything bound/unbound).
|
|
611
|
+
- `update` body shape is identical to `create`'s but each rule **must** include `rule_id`. Recommended workflow: `rule info` to pull → edit JSON → `rule update --dry-run` → `rule update`.
|
|
612
|
+
- `status update` is the bulk lifecycle endpoint: `--operate-type=TURN_ON|TURN_OFF|DELETE` + `--rule-ids=X,Y,Z`. **Atomic**: if any rule fails, the whole batch rolls back. Client-side enum validation catches bad operate_type early.
|
|
613
|
+
- `batch-bind` attaches/detaches existing rules to objects without rewriting rule definitions. Two input modes: convenience flags (`--rule-id/--dimension/--dimension-ids/--bind-type` for one entry) or `--body / --body-from-file` (multi-entry). **Smart+ Upgraded ads are NOT supported by this endpoint** — TikTok returns `code=40002 This API does not support Upgraded Smart Plus ads.` on Smart+ campaigns; for those, edit `apply_objects` via `rule update` instead.
|
|
614
|
+
- DELETE is soft-delete — deleted rules still show up via `optimizer rule list --status=DELETED`.
|
|
615
|
+
- Drill-down chain: `rule list` (definitions) → `rule result list` (each execution's aggregate counts) → `rule result info` (each affected object's SUCCESS/FAIL + error_msg)
|
|
616
|
+
- `info` (by IDs) and `list` (by filters) return rule definitions; `result list` returns execution history; `result info` returns per-object outcomes for given (rule_id, exec_id) pairs
|
|
617
|
+
- Common shapes shown in `ttcli optimizer rule create --help` (message-only rule, budget-adjust rule with frequency limit)
|
|
618
|
+
- Permission gate split: `list` and `result list` are open (return empty array if no data); `create`, `update`, `status update`, `batch-bind`, `info`, and `result info` require BC admin to grant `optimizer` resource scope (without it return 40002 / 52404). Note: `update`, `status update`, and `batch-bind` reach **business-level validation** (TikTok parses the body and validates rule_id/object existence) rather than failing at the scope layer.
|
|
619
|
+
- Filter shape: `--status=ON|OFF|DELETED`, `--rule-info=ID,name`, `--data-dimension=CAMPAIGN|ADGROUP|AD`. `result list` adds `--action=TURN_ON|TURN_OFF|MESSAGE|DAILY_BUDGET|LIFETIME_BUDGET|BID` (TikTok docs typo it as LIFERIME_BUDGET — use the correct LIFETIME_BUDGET) and `--time='start,end'` (UTC, format YYYY-MM-DD HH:MM:SS). cmd encodes as `filtering` JSON object (TikTok doc shows v1.2 `filters: [{filter_type, filter_input}]` in curl example, but real v1.3 uses `filtering` per the parameter table).
|
|
620
|
+
- `result info` takes paired flags: `--rule-ids` and `--exec-ids` must have equal length; index N pairs them into the `result_detail` array TikTok expects.
|
|
621
|
+
- Field-name split inside `rules[]`: `info`/`list` populate `last_check_result_summary`; `result list` populates `rule_check_result` (same nested struct). Use `Rule.CheckResult()` helper to ignore the difference.
|
|
622
|
+
|
|
383
623
|
## Idempotency: reuse request_id on retry
|
|
384
624
|
|
|
385
625
|
Without `--request-id`, ttcli generates one from `time.Now().UnixNano()` and logs it to stderr:
|
|
@@ -465,6 +705,8 @@ Limits: 1 QPS per app; 500 async tasks/hour per advertiser; 30-day task validity
|
|
|
465
705
|
|
|
466
706
|
## Credentials
|
|
467
707
|
|
|
708
|
+
**主流程**:token 走 vhcli bridge(见上面"🚨 Token 走 vhcli bridge"那节)。下面是 ttcli 自己的 credential 解析链——开发者本地调试或排查"用了哪个 token"时会用到。
|
|
709
|
+
|
|
468
710
|
Priority: `--flag` > env var > config file (`~/.config/ttcli/credentials.json`, mode 0600).
|
|
469
711
|
|
|
470
712
|
| Field | Flag | Env |
|
|
@@ -473,13 +715,9 @@ Priority: `--flag` > env var > config file (`~/.config/ttcli/credentials.json`,
|
|
|
473
715
|
| Advertiser ID | `--advertiser-id` | `TIKTOK_ADVERTISER_ID` |
|
|
474
716
|
| Base URL | `--base-url` | `TIKTOK_BASE_URL` |
|
|
475
717
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
```
|
|
479
|
-
ttcli auth set --token=... --advertiser-id=...
|
|
480
|
-
```
|
|
718
|
+
bridge 流程里 token 是从 vhcli stdout 直接 pipe 进 `--token=` 或 `ttcli auth set`,前者临时(推荐多账户场景)后者持久化(推荐单账户长期场景)。
|
|
481
719
|
|
|
482
|
-
`auth show`
|
|
720
|
+
`auth show` 报每个值的来源(`flag` / `env:VAR` / `file` / `default` / `unset`);当一次写操作报 `40001 No permission` 而你怀疑用错了广告主,先 `ttcli auth show` 确认。
|
|
483
721
|
|
|
484
722
|
## Standard end-to-end flow
|
|
485
723
|
|