← 全記事
チュートリアル

UnifyPortで複数メッセージの配信・既読通知を処理する

UnifyPortの配信通知や既読通知は、複数のメッセージを対象にする場合があります。一括通知では data.message.ids のすべてのIDを処理し、単一メッセージの通知では data.message.id を使います。通知を本文とは別に保存すれば、通知が先に届いても保持できます。送信の受付、受信者の既読、チームの受信トレイの既読状態は、別々の証拠です。

要点

  • メッセージIDだけでなく、ワークスペース、プロバイダー、メッセージングアカウント、会話で照合します。
  • プロバイダーの通知時刻とwebhookの到着時刻を分けて保存します。
  • 遅れて届いた配信通知で、確認済みの既読状態を戻さないようにします。
  • 通知がない場合は確認不明であり、再送の根拠にはなりません。

表示を更新する前に通知の対象を確認する

標準イベント仕様では、message.delivered は受信者の端末への配信、message.read は受信者の既読通知を表します。一括通知の対象は data.message.ids です。単数の id だけを更新すると、残りのメッセージを取りこぼします。

証拠確認できること確認できないこと
送信応答の status: acceptedリクエストの受付配信・既読
message.delivered指定メッセージの配信受信者が読んだこと
message.read指定メッセージの既読問い合わせの解決、グループ全員の既読
会話のローカル既読状態接続アカウントのチャット一覧の状態受信者の既読通知

最後の項目は、別の共有受信トレイの既読・未読ワークフローで扱います。送信メッセージの通知がないからといって、会話を既読にする操作で修復しようとしないでください。

以下は既読通知リファレンスのフィールドを使った短縮例です。実際の顧客イベントではありません。

{
  "id": "evt_2f9c1a4b7e",
  "type": "message.read",
  "provider": "whatsapp",
  "account_id": "acc_8c21d0",
  "occurred_at": "2026-06-08T12:35:10Z",
  "data": {
    "conversation": { "id": "8613912345678", "type": "user" },
    "sender": { "id": "8613912345678", "type": "user" },
    "message": {
      "id": "wamid.HBgM",
      "ids": ["wamid.HBgM", "wamid.HBgN"],
      "read_at": "2026-06-08T12:35:10Z"
    }
  }
}

対象は列挙された2件です。それ以前のすべてのメッセージが既読になったという意味ではありません。IDが数字に見えても、連続した範囲を推定しないでください。

観測記録を保存してから状態表示を作る

推奨する構成は、署名検証済みイベントの永続記録と、メッセージ単位の通知ビューの2層です。これはアプリケーションの保存設計であり、UnifyPortの追加フィールドではありません。

  1. 必要な既存イベントと署名設定を維持し、両方の通知イベント名を購読に追加します。
  2. webhook配信仕様に従い、タイムスタンプ、ドット、未加工本文を連結したデータのHMAC-SHA256を signing_secret で検証します。署名タイムスタンプの鮮度も確認します。
  3. 検証済みイベントを永続化してから2xxを返します。イベントと永続ジョブを原子的に保存するか、ビュー更新と重複排除記録を同一トランザクションで確定します。
  4. 一括通知をメッセージごとの観測記録へ展開します。ワークスペース情報、provider、account_id、data.conversation.id、イベントID、利用可能な送信者識別情報を保持します。
  5. occurred_at、プロバイダーの read_at または delivered_at、アプリケーション側の到着時刻を別々に保存します。通知時刻は data 階層の同名フィールドにも反映されます。時刻がなければ不明のままにし、到着時刻で代用しません。

次のJavaScriptは、検証済みイベントから対象IDだけを取り出します。完全な受信サーバーやデータベース実装ではありません。

function receiptMessageIds(event) {
  if (!['message.delivered', 'message.read'].includes(event.type)) return [];
  const message = event.data?.message;
  const ids = message?.ids === undefined ? [message?.id] : message.ids;
  if (!Array.isArray(ids) || ids.length === 0 ||
      ids.some(id => typeof id !== 'string' || id.length === 0)) {
    throw new Error('Receipt scope needs review');
  }
  return [...new Set(ids)];
}

一括フィールドが不正な場合、黙って単数IDに切り替えないでください。対象範囲を推測して更新せず、イベントを調査用に保持します。

配信の証拠を作らずに統合する

配信と既読の観測は独立して保持します。既読通知が先に届いた場合は「既読を確認」と表示できますが、delivered_at を作ってはいけません。後から届いた配信通知は証拠を追加するだけで、表示を「配信済み」に戻しません。重複イベントで通知を二重発行しないことも重要です。

本文が未保存なら、完全なメッセージ識別情報をキーに通知を保持し、対応するレコードが届いたら関連付けます。通知だけで受信メッセージを新規作成したり、受信件数を増やしたりしません。occurred_at とイベントIDで決定的な処理順を作れますが、最後に到着した状態で一律に上書きしないでください。

送信時には、応答の message_id と provider_ref をローカル操作と一緒に保存します。通知を関連付ける前に、どの識別子が観測したプロバイダーメッセージに対応するか確認します。名前の異なるフィールド同士が常に等しい保証はありません。照合できない通知は確認キューに入れ、時刻が近いメッセージへ便宜的に付けないでください。送信者識別情報も保持し、1件の観測を「全員既読」に広げないようにします。

メディア送信のトラブルシューティングは、ファイル取得や受付段階を扱います。通知処理はその先の別の層です。タイムアウトや通知欠落を自動再送に直結させないでください。

LINEを含む対応範囲とテスト

UnifyPortは非公式インターフェースです。プロバイダー別イベント表はパーサーの対応状況であり、すべてのアカウントや環境から通知が届く保証ではありません。LINEを使う受信トレイでも、状態表示を約束する前に対象プロバイダーを確認してください。通知がないだけでは、未読、配信失敗、ブロックのいずれも証明できません。取り逃したイベントの再配信も保証されません。

推奨テストは、2件を対象にした一括通知、単一通知、重複、既読が配信より先に到着する場合、通知が本文より先に到着する場合、別アカウントの同じID、不正な一括フィールド、通知が届かない場合です。明示されたIDだけが更新され、再送が発生しないことを確認します。これは実施すべきテストであり、実測結果ではありません。

よくある質問

idとidsのどちらを処理しますか?

有効かつ空でない一括 ids があれば使い、それ以外は単数 id を使います。不正な対象範囲は調査対象です。

既読通知からdelivered_atを補完できますか?

できません。観測した既読は表示できますが、プロバイダーの配信時刻を作ってはいけません。

通知がなければ送信失敗にできますか?

それだけでは判断できません。確認不明として、対応範囲、購読、受信経路、ID照合を調べます。

次のステップと資料

配信通知リファレンスを起点に、状態表示より先に対象範囲を保持する観測ストアを実装してください。

製品資料の確認日:2026-11-06。

UnifyPort API

メッセージ連携を安定したプロダクトパイプラインへ。

まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。