HAiNA CLI docs
接入指南

TikTok Shop 挂车发布全流程

上传视频(拿 fileId)→ 拉商品 →(可选)预审 → 挂车发布

TikTok Shop 挂车发布全流程

流程总览

上传视频(TikTok Shop 库,拿一次性 fileId)→ 拉可挂车商品(拿 productId)
  →(可选)预审(precheck,自检违规)→ 挂车发布 → 轮询终态

全程只需官方返回的 id(fileId / creatorUserOpenId / productId / taskId),平台不引入任何自造 id。

⚠️ 四条硬规则(TikTok Shop 官方)

这些规则由官方在发布时校验(平台不预检,违规透传官方报错 3001,message 带官方原因):

  1. fileId 一次性消耗:发布调用即消耗,失败也不能复用。重发必须重新上传视频。 另外 fileId 与上传时的账号绑定(官方规则):A 账号上传的视频只能由 A 账号发布——发布时的 accountId 必须与上传时一致。
  2. 锚点文案(productTitle):必填,≤30 个字符,不能有标点和 emoji(车用脚垫 ✅、好货! ❌)。
  3. 商品必须可挂车:reviewStatus=APPROVED 且有货;商品需与视频内容相关。
  4. 预审可选但强烈建议(MRD IF-161144):发布接口不要求预审凭证(与官方一致);但官方规则要求违规内容不应发布——status=failed(violation FAIL)时请修正素材重审,是否发布由你自行判断并承担后果。优质检查(good quality)未过不阻断,仅给优化建议。预审配额 50 次/天/账号,耗尽返回 2002。

字段级补充(同为官方校验):

  • title(视频文案):支持 #话题 与 @提及(@提及 仅互关好友生效,App 内显示为纯文本)
  • isAiGenerated:AI 生成内容必须传 true(发布后会带 AI 标识)

下文示例中 $BASE 为生产环境地址(https://cli.haina.io),$TOKEN 为「API Keys」页创建的 API Token。

第 1 步:上传视频到 TikTok Shop 库

比 TikTok 库多一个表单字段 accountId(= 坐席里 TikTok Shop 绑定的 creatorUserOpenId):

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@demo.mp4" \
  -F "library=tts" \
  -F "accountId=<creatorUserOpenId>" \
  $BASE/v1/videos

返回 201:

{
  "code": 0,
  "data": {
    "videoId": "01a08b67-d2e9-7f9-8eebde-d63b0c18",
    "fileId": "v12d00gd0024dahajs7og65ihgiis770",
    "videoUrl": null,
    "expiresAt": null
  }
}

TikTok Shop 视频存入 TikTok Shop 平台侧,没有 URL 过期概念;生命周期就是「待发布 → 已发布」。

两个 id 的分工:fileId 是官方口径,预审/发布都用它;videoId 只是视频库管理 id(查询/删除 /v1/videos/{id} 用),发布链路不需要。

视频规则汇总(格式/大小/官方规格等硬约束):

项规则
格式mp4 / mov(Content-Type video/mp4 / video/quicktime;官方还支持 webm,本平台只收前两种)
大小≤ 100MB(超限 2001;官方上限 1GB,本平台收紧)
TikTok 视频官方规格时长 3–600 秒;宽高均 ≥360px;帧率 23–60 FPS(不满足会在发布阶段被官方拒)
TikTok 视频有效期videoUrl 上传后 7 天有效(平台计时);到期不可发布(2005),需重新上传
TikTok Shop 视频fileId 一次性消耗:发布调用即作废(失败也不可复用),重发需重新上传;无 URL 过期概念
发布频次TikTok 发布:官方限制每账号 ≤6 个/分钟、≤15 个/天(批量发布请自行控制节奏,超限会被官方拒绝);TikTok Shop 挂车:平台侧配额(不同账号不同),超限官方报错,发布成功响应可能带 quota 字段提示剩余配额
TikTok Shop 预审配额50 次/天/账号(API 渠道独立计数;同一 fileId 重复提交会多次消耗);耗尽返回 2002,次日恢复

第 2 步:拉取可挂车商品

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"accountId":"<creatorUserOpenId>","productType":"shop","pageSize":20}' \
  $BASE/v1/products/query
  • productType:shop(店铺商品)/ showcase(橱窗);省略 = 两组都返回
  • 分页:拿响应里的 nextPageToken 传入下一次的 pageToken;翻页必须在单类型下进行(两组游标互相独立)
  • 数据有 60s 短缓存;加 "fresh": true 穿透缓存直取官方
  • 前置条件:挂车账号需已在 TikTok Shop 后台配置「选区(selection region)」——未配置时 shop 面报 3001 invalid params、showcase 面报官方明确提示(no selection region)。这是账号配置项(LOCAL 型账号常见),去卖家后台配置后重试

从 products[].id 拿到 productId。商品同步到自己系统的做法见商品同步集成食谱。

第 3 步:预审(可选,建议做)

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fileId": "v12d00gd0024dahajs7og65ihgiis770",
    "accountId": "<creatorUserOpenId>",
    "productId": "1732130661919265505",
    "productTitle": "车用脚垫"
  }' \
  $BASE/v1/publish/tts/precheck

返回 201:{ "data": { "taskId": "…", "status": "pending" } }(taskId 是官方预审任务 ID,请自行保存)

轮询结果(pending 时服务端会先拉一次官方):

curl -H "Authorization: Bearer $TOKEN" \
  $BASE/v1/publish/tts/precheck/<taskId>
  • status=passed → 预审通过(qualitySuggestions 非空只是优化建议,不影响发布)
  • status=failed → 官方判定违规(violationCheckResult.issues 含违规类型与建议;建议不要发布,修正素材后重新上传+预审)
  • status=pending → 稍后重查

预审结果长这样(官方双 check 结构的真实示例):

{
  "code": 0,
  "data": {
    "taskId": "7493990579714164574",
    "status": "failed",
    "violationCheckResult": {
      "status": "FAIL",
      "issues": [
        {
          "risk": "Pirated Content",
          "suggestions": "Your video may include unoriginal content. Creating original content is essential for standing out from the crowd."
        }
      ]
    },
    "goodQualityCheckResult": {
      "status": "FAIL",
      "issues": [
        {
          "code": "LOW_CONTENT_PROMOTIO",
          "suggestions": "You can try to showcase the product from multiple perspectives."
        }
      ]
    },
    "qualitySuggestions": []
  }
}

怎么读:violationCheckResult 是红线(违规,issues 里的 risk 是违规类型、suggestions 是官方整改建议);goodQualityCheckResult 是加分项(code 是质量短板类型,比如内容促销感不足,FAIL 只意味着「不够优质」,不影响发布资格)。

第 3.5 步(可选):封面图与背景音乐

封面图:上传图片拿 photoUri,发布时传 coverUri(也可只传 coverTimestampMs 截帧;都不传默认首帧):

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -F "file=@cover.jpg;type=image/jpeg" -F "accountId=<creatorUserOpenId>" \
  $BASE/v1/photos
# → { "photoUri": "tos-useast5-…" }(JPG/PNG/WEBP/HEIC/BMP,≤10MB,宽高比 9:16~16:9)

背景音乐:搜索电商授权音乐拿 id,发布时传 musicId:

⚠️ 官方规则:传了 musicId 会完全覆盖视频原声(包括口播人声),不是混音。 口播讲解类视频不要配 BGM。

curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"accountId":"<creatorUserOpenId>","keyword":"love","pageSize":20,"region":"US","language":"en-US"}' \
  $BASE/v1/music/search
# → { music: [{id,title,author,coverUrls,playUrls,duration}], hasMore, nextPageToken, searchId }
# 翻页:第 2 页起必须带首页返回的 searchId + pageToken

官方分页怪癖(实测):nextPageToken 是数值 offset(按 pageSize 递增),原值回传即可;searchId 每次调用都会变,一次翻页链必须在同一次搜索会话内连续传递;带小 pageSize 的首调用可能返回空数组但 hasMore=true——别误判无结果,带返回的 token+searchId 再查一次即得数据。

后台「素材工具」页提供可视化上传/搜索/试听/复制(无需写脚本)。

第 4 步:挂车发布

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "fileId": "v12d00gd0024dahajs7og65ihgiis770",
    "accountId": "<creatorUserOpenId>",
    "productId": "1732130661919265505",
    "title": "视频文案 #话题",
    "productTitle": "车用脚垫",
    "isAiGenerated": false,
    "coverUri": "tos-useast5-…",
    "musicId": "7086563693013518338"
  }' \
  $BASE/v1/publish/tts

返回 201:

{
  "code": 0,
  "data": {
    "publishId": "01a08b68-7b81-725-a1666f-47ce00e9",
    "capability": "TTS",
    "videoId": "7683890882440908046",
    "status": "publish_complete",
    "quota": "3/day"
  }
}
  • 发布无需预审凭证(与官方一致);是否先预审由你决定。
  • quota 为创作者发布配额提示,仅当平台有配额限制时返回;不返回 ≠ 错误。

字段与官方 Post Shoppable Video 的对应关系(如果你在读官方文档,一眼就能对上):

本平台字段官方字段(v202607)来源
fileIdvideo_info.file_idUpload Video File 返回
titlevideo_info.title视频文案(官方必填,缺省报 3001;支持 #话题 @提及)
isAiGeneratedvideo_info.is_ai_generatedAI 标识
coverUrivideo_info.cover_uriUpload Shoppable Photo File 返回
coverTimestampMsvideo_info.cover_timestamp_ms封面帧毫秒
musicIdvideo_info.music_idSearch Music 返回
productIdproduct_link_info.product_id商品接口返回
productTitleproduct_link_info.title锚点文案
accountId官方走 access token 识别本平台按坐席账号显式指定
  • videoId 即 TikTok Shop 侧的视频 ID(官方 video_id)。用它轮询终态(TTS 无 webhook,轮询是唯一路径):
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/v1/publish/tts/status?videoId=7683890882440908046"

每 5–10 秒查一次,终态为 publish_complete / publish_failed(失败原因在 failReason)。后台「发布记录」页对进行中记录也会每 10s 自动收敛。

⚠️ 发布记录(/v1/publish/records)里的 videoId 是平台视频库 ID,别拿它查状态(必 1005);TikTok Shop 记录的官方 video_id 在记录的 shareId 字段。

发布后视频在视频库中标记为「已发布」,同一 fileId 再次发布会被官方拒绝(fileId 已消耗,官方报错透传)。

第 5 步(可选):数据回收

挂车视频发布后就是账号上的一条普通帖子(带商品锚点),播放/点赞等指标走「数据洞察」面:

haina tt insight videos --account <同坐席 TikTok 账号>   # 拉账号视频列表(含指标),按标题/发布时间找到这条挂车视频

⚠️ 两个注意:① TikTok Shop 发布记录里没有 TikTok item_id(官方挂车状态响应只有 TikTok Shop 侧 video_id)——video/list 没有标题/时间筛选器,定位方式是拉列表后在返回行里按 caption/create_time 自行匹配(列表按发布时间倒序,新发的视频就在第一页);② 该坐席需绑了 TikTok 能力(洞察走 TikTok 发布体系授权),仅绑 TikTok Shop 的坐席无数据回收面。

旧端点 GET /v1/publish/stats(将要废弃,1.6.x 移除)本就不支持 TikTok Shop 记录(它是 TikTok 发布专属的记录锚定接口)——挂车数据回收请一律走上面的数据洞察路由。

On this page