← 全記事
チュートリアル

TikTok Data Portability とリアルタイムDMは別物:サポートに必要な受信キューを作る

TikTok の開発者向け API は細かく整備されています。ただし、ドキュメントが明確になったからといって、サポートチームが必要としているリアルタイム運用まで解決するとは限りません。

6 月 4 日、TikTok は Data Portability API changelog で、Data Types ドキュメントを現在サポートされているカテゴリとフィールドに合わせて更新したと説明しました。Data Portability API の製品ページでは、この API が欧州経済領域と英国の TikTok ユーザーに対して、ユーザーが自分の情報を別アプリへ転送する許可を与えられる仕組みだと説明されています。現在の data types ページ には、Direct Messages もエクスポート対象として掲載され、日付、送信者、内容などのフィールドが示されています。

これはデータポータビリティ、アーカイブ、バックアップ、コンプライアンスには有用です。しかし、リアルタイムのカスタマーサポート受信箱ではありません。

TikTok Shop、クリエイター施策、ライブ販売を運用する小規模チームが必要としているのは、別の仕組みです。新しいメッセージが届いたら、数秒でキューに入り、重複排除され、CRM 照会が走り、担当者または AI アシスタントに振り分けられる必要があります。ユーザーが許可した過去データのエクスポートでは、このイベントループは作れません。署名付きの受信 webhook なら作れます。

まず統合に任せる仕事を定義する

API を選ぶ前に、やりたい仕事を普通の言葉で書きます。

  1. 顧客が TikTok のダイレクトメッセージを送る。
  2. バックエンドが数秒以内にそのメッセージを受け取る。
  3. 配信には署名があり、サーバーが信頼できることを確認できる。
  4. 逃したイベントは後から戻せないため、先に保存する。
  5. 同じキューで、将来 WhatsApp、LINE、Zalo、Telegram、X も受けられる。

TikTok Data Portability API は、この順番のために作られたものではありません。これはユーザー同意に基づくデータ転送の製品です。申請者は EEA または UK のユーザーにサービスを提供し、プライバシーとセキュリティ審査を通過し、定義済みのデータスコープを申請します。モデルはエクスポート中心です。投稿とプロフィール、アクティビティ、ダイレクトメッセージ、またはフルアーカイブです。

サポートに必要な時間軸とは違います。サポートが聞いているのは「昨日のアーカイブを取得できるか」ではなく、「10 秒前に届いたメッセージに今反応できるか」です。

UnifyPort webhook の形

UnifyPort の TikTok unofficial interface は、後者のモデルに向いています。TikTok アカウントを接続し、webhook endpoint を登録し、message.received を購読します。顧客メッセージが届くと、UnifyPort は標準化されたイベントをバックエンドへ送ります。

{
  "id": "evt_7a4d2c91b6",
  "type": "message.received",
  "provider": "tiktok",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-07-05T03:18:42Z",
  "data": {
    "conversation": { "id": "tt_conv_9172", "type": "user", "title": "Mai Nguyen" },
    "sender": { "id": "tt_user_4839", "name": "Mai Nguyen", "type": "user" },
    "message": {
      "id": "tt_msg_20260705_001",
      "type": "text",
      "text": "今日のライブ前に黒のトートはまだ買えますか?",
      "direction": "inbound",
      "sent_at": "2026-07-05T03:18:41Z"
    }
  }
}

重要なのは、idtypeprovideraccount_idoccurred_atdata という同じ封筒で届くことです。同じハンドラーで今日は provider: "tiktok"、明日は provider: "line" を処理できます。LINE が強い日本市場でも、受信処理を LINE 専用に閉じないことが後から効きます。

endpoint を先に登録する

キューにつなぐ前に webhook を作成します。endpoint には URL、購読イベント、署名状態、リトライポリシーが保存されます。signing_secret を設定すると、各配信に X-Device-TimestampX-Device-Signature が付きます。

curl -X POST https://api.unifyport.ai/v1/webhook-endpoints \
  -H "X-Api-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/webhook",
  "status": "active",
  "subscribed_events": ["message.received"],
  "signing_secret": "<WEBHOOK_SIGNING_SECRET>"
}'

UnifyPort はタイムスタンプ、ドット、raw request body を HMAC-SHA256 で署名します。JSON を解析する前に、必ず raw bytes に対して検証してください。先に解析して再シリアライズすると、検証対象の bytes が変わります。

import crypto from "crypto";
import express from "express";

const app = express();
const signingSecret = process.env.WEBHOOK_SIGNING_SECRET;

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const timestamp = req.get("X-Device-Timestamp");
  const signature = req.get("X-Device-Signature");

  const hmac = crypto.createHmac("sha256", signingSecret);
  hmac.update(timestamp);
  hmac.update(".");
  hmac.update(req.body);
  const expected = hmac.digest("hex");

  const valid =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!valid) {
    res.status(401).end();
    return;
  }

  const event = JSON.parse(req.body.toString("utf8"));
  if (event.type === "message.received") {
    console.log(event.provider, event.data.sender.id, event.data.message.text);
  }

  res.status(200).end();
});

app.listen(3000);

これで受信エッジは成立します。本番ではイベントをキューに積みますが、境界は変わりません。署名付き配信を受け取り、検証済みイベントとして流します。

先に保存し、後でルーティングする

UnifyPort のドキュメントは、webhook event が受信トラフィックの唯一の記録であると明記しています。メッセージ読み取り API はなく、逃した payload を後から補完する経路もありません。そのため、キュー設計は保存を最初に置くべきです。

最初の書き込みでは、id をキーにイベントを保存し、raw body または解析済み JSON、provider、account ID、occurred_at を残します。書き込み後に、非同期で振り分けます。

TikTok message
  -> UnifyPort message.received webhook
  -> Signature verification
  -> Event store keyed by id
  -> Routing queue
  -> CRM lookup, Slack alert, helpdesk ticket, or AI triage

Data Portability API は、ここで役割がはっきりします。エクスポートは、ユーザー同意に基づくデータ移動のためのものです。リアルタイムのサポートキューは運用のためのものです。両方を使うことはできますが、同じものとして扱うべきではありません。

返信は必要になってから追加する

チームによっては受信の振り分けだけで十分です。別のチームでは、人間または AI アシスタントが判断した後に返信したいこともあります。返信は明確な第 2 ステップとして扱います。

送信には POST /v1/messages を使い、アカウント、宛先、標準化されたメッセージ本文を渡します。受信イベントは provider、account、sender、conversation、message text を渡してくれます。返信ワークフローは、その後に何を送るかを決めます。

curl -X POST https://api.unifyport.ai/v1/messages \
  -H "X-Api-Key: <YOUR_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
  "account_id": "acc_8c21d0",
  "to": { "id": "tt_user_4839", "type": "user" },
  "message": { "type": "text", "text": "はい、黒のトートはまだ在庫があります。" }
}'

受信と送信はコード上でも分けておきます。最初の経路は顧客メッセージを捕捉して保存します。次の経路は、ビジネスロジックが決めた場合だけ返信します。

2026 年 7 月にこの区別が重要な理由

TikTok の 6 月 4 日の更新は、プラットフォーム API がそれぞれ特定の政策面または製品面に向いていることを思い出させます。Data portability はユーザー主導のデータ転送です。Content Posting は投稿です。Display API はクリエイターコンテンツの表示です。どれも自動的に「リアルタイムのサポートDM受信箱」にはなりません。

小規模チームが時間を失うのは、新しく文書化された API カテゴリをすべてカスタマーサポートのイベントストリームだと見なしてしまうときです。先にワークフローを名付け、その時間軸に合う interface を選ぶほうが安全です。

ワークフローがエクスポートなら、エクスポート用の API を使います。サポートなら、webhook を使います。今日 TikTok、来月 LINE や Zalo も扱うなら、最初から標準化された webhook envelope にしておきます。

実務上の分担は明確です。TikTok Data Portability は、ユーザーが自分のデータを移動するためのものです。UnifyPort は、顧客メッセージが届いた瞬間にサポートシステムへ届けるためのものです。