Webhook連携
SORI APIのWebhookは、SORIプラットフォーム内で発生する主要なイベントを自社システムに通知します。エンドポイントを接続することで、キャンペーン活動、デバイス認証、管理操作をほぼリアルタイムに処理できます。
概要
SORI APIは、重要なイベントが発生した直後に構造化されたJSONペイロードを配信するWebhook機能を提供します。WebhookはSORI Consoleで設定でき、次のカテゴリをサポートします:
- キャンペーンイベント(`campaign.*`) デバイスでキャンペーンが認識されたときや、ユーザーがアクションURLに遷移したときにトリガーされます。
- 認証イベント(`authentication`) デバイスがSORIの認証フローを完了したときに発生します。
- 管理イベント(`admin.material.*`) コンソールでマテリアルを作成・更新・削除した際に送信される任意の通知です。
リアルタイム通知によって以下が可能になります:
- インスタント分析 活動が起きた瞬間にキャンペーン指標を追跡・分析します。
- 認証監査 デバイスのログインを自社のセッション記録と突合します。
- 不正検知 異常なパターンを即座に検出します。
- ライブダッシュボード 最新データでダッシュボードを更新します。
Webhookの設定
SORIでは、Webhookエンドポイントを次の2通りで構成できます。
グローバルWebhook URL
- 設定セクションで設定します。
- 既定で全キャンペーンに適用されます。
- キャンペーンが個別URLを指定しない場合のフォールバックとして機能します。
キャンペーン固有のWebhook URL
- 各キャンペーンの詳細設定で構成します。
- 該当キャンペーンではグローバルURLより優先されます。
- キャンペーンごとに異なる処理が必要な場合に便利です。
イベント購読(設定 → Webhooks)
アカウント設定では、どのイベントカテゴリを受信するかを選択できます。
- キャンペーンイベント:
campaign.*通知(インプレッションとクリック活動)を購読します。 - 認証イベント: デバイスログイン時に送信される
authenticationイベントを購読します。 - 管理イベント: コンソールから送信される
admin.material.*通知を購読します。
新規アカウントではキャンペーンイベントと認証イベントが既定で有効です。管理者はチェックボックスをいつでも変更でき、以後のWebhook配信に即時反映されます。
Webhookを設定する手順
- Webhookイベントを受信するHTTPSエンドポイントを準備します。
- 次のいずれかを構成します。
- 設定画面でグローバルWebhook URLを登録する。
- 各キャンペーンの設定で個別のWebhook URLを登録する。
- 設定 → Webhooks で受信したいイベントカテゴリを選択します。
- JSON形式のPOSTリクエストを受け取り、5秒以内に応答できるようエンドポイントを実装します。
イベントタイプ
各Webhookペイロードには正規化された event フィールドが含まれます。新規実装ではこの値を基準に処理することを推奨します。
キャンペーンインプレッションイベント(campaign.impression)
キャンペーンがユーザーのデバイスに表示されたときに送信されます。キャンペーンイベント を有効にすると受信できます。
キャンペーンクリックイベント(campaign.click)
ユーザーがキャンペーンをタップしてアクションURLに遷移したときに送信されます。キャンペーンイベント を有効にすると受信できます。
認証イベント(authentication)
デバイスがSORIでの認証を完了した後に送信されます。認証リクエストのクエリパラメーターが metadata に含まれるため、自社のセッションIDなどと突合できます。
Webhookイベントの処理
Webhookイベントを受信すると、イベントの詳細を含むJSONペイロードが送信されます。以下に各イベントタイプの例を示します。null のフィールドは値がない場合は省略される場合があります。
ペイロードスキーマ
特に注記がない限り、キャンペーンWebhookには以下のフィールドが含まれます。
| フィールド | 型 | 説明 |
|---|---|---|
event | string | campaign.impression、campaign.click、authentication などの正規イベント名です。 |
event_type | string | 後方互換性のために残されている event の複製です。 |
account_id | string | キャンペーンを所有するSORIアカウントの識別子です。 |
created_at | ISO 8601 string | イベントオブジェクトがサーバーで生成された時刻です。 |
timestamp | ISO 8601 string | 配信直前に付与される時刻で、冪等性チェックに利用できます。 |
activity_id | string | 個々のインプレッション/クリックを区別するIDです(キャンペーンイベントのみ)。 |
device_id | string | イベントを発生させたデバイスまたはセッションです。 |
platform | string | SDKが報告するクライアントプラットフォーム(Android、iOS など)です。 |
metadata | object | SDKまたは認証リクエストから渡されるカスタムのキーと値です。 |
geolocation.viewer_country | string | CloudFrontのGeoIPから推定した国情報(取得できない場合は Unknown)。 |
geolocation.viewer_region | string | GeoIPから推定した地域/州情報です。 |
geolocation.viewer_city | string | GeoIPから推定した都市情報です。 |
campaign.id | string | コンソールに表示されるキャンペーンIDと一致する正規IDです。 |
campaign.name | string | イベント発生時点のキャンペーン表示名です。 |
campaign.tags | string[] | キャンペーンに設定されたタグ配列で、未設定の場合は空配列です。 |
campaign.image | string (URL) | 設定されている場合のキャンペーンサムネイルHTTPS URLです。 |
material.id | string | 認識されたマテリアルのIDです(キャンペーンイベント)。 |
material.name | string | マテリアル名で、該当マテリアルがない場合は省略されます。 |
material.tags | string[] | マテリアルに保存されたタグ配列で、未設定の場合は空配列です。 |
material.image | string (URL) | 利用可能な場合のマテリアルプレビュー画像URLです。 |
レガシーフィールド
event_type、campaign_id、campaign_name、material_id、material_name の トップレベルフィールドは後方互換性のために残されていますが、今後削除 される予定です。新しい連携では event フィールドと campaign/material オブジェクトを使用してください。以下のサンプルでは分かりやすさのため にレガシーキーを省いています。
インプレッションイベントの例:
{
"event": "campaign.impression",
"account_id": "acc_123456",
"created_at": "2025-10-14T05:25:12.421Z",
"activity_id": "act_7890",
"device_id": "device_abc",
"platform": "Android",
"metadata": {
"session": "abcd-1234"
},
"geolocation": {
"viewer_country": "United States",
"viewer_region": "Michigan",
"viewer_city": "Ann Arbor"
},
"campaign": {
"id": "cmp_1234",
"name": "Fall Promotion",
"tags": [
"retail",
"autumn"
],
"image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
},
"material": {
"id": "mat_5678",
"name": "Audio Spot 15s",
"tags": [
"audio",
"15s"
],
"image": "https://cdn.soriapi.com/materials/mat_5678.png"
},
"timestamp": "2025-10-14T05:25:12.421Z"
}クリックイベントの例:
{
"event": "campaign.click",
"account_id": "acc_123456",
"created_at": "2025-10-14T05:26:03.009Z",
"activity_id": "act_7890",
"device_id": "device_abc",
"platform": "Android",
"metadata": {
"session": "abcd-1234",
"utm_source": "push"
},
"geolocation": {
"viewer_country": "United States",
"viewer_region": "Michigan",
"viewer_city": "Ann Arbor"
},
"campaign": {
"id": "cmp_1234",
"name": "Fall Promotion",
"tags": [
"retail",
"autumn"
],
"image": "https://cdn.soriapi.com/campaigns/cmp_1234.png"
},
"material": {
"id": "mat_5678",
"name": "Audio Spot 15s",
"tags": [
"audio",
"15s"
],
"image": "https://cdn.soriapi.com/materials/mat_5678.png"
},
"timestamp": "2025-10-14T05:26:03.009Z"
}認証イベントの例:
{
"event": "authentication",
"account_id": "acc_123456",
"created_at": "2025-10-14T05:15:44.832Z",
"device_id": "device_abc",
"platform": "Android",
"metadata": {
"app_version": "1.8.0",
"session": "abcd-1234"
},
"geolocation": {
"viewer_country": "United States",
"viewer_region": "Michigan",
"viewer_city": "Ann Arbor"
},
"timestamp": "2025-10-14T05:15:44.832Z"
}カスタムメタデータ
metadata フィールドは、キャンペーンイベントではSDKから送信されるキーと値、認証イベントでは認証リクエストのクエリパラメーターが格納されるマップです。ユーザーやデバイスを識別したり、追加のコンテキストを渡したりするために利用できます。このフィールドは任意です。詳細はMetadata Providerを参照してください。
位置情報について
位置情報は geolocation オブジェクトの中に含まれます。このデータはCloudFrontのGeoIP推定に基づくため、常に正確とは限りません。値が取得できない場合は Unknown が返されます。
レスポンス要件
Webhookエンドポイントでは次の点を満たしてください。
2xxステータスコードで受信を確認する。- 可能であれば非同期で処理し、処理待ちでブロックしない。
- タイムスタンプやアクティビティIDを利用して冪等に処理する。
- タイムアウトを避けるため 5秒以内 に応答する。
セキュリティ考慮事項
- 可能な限りHTTPSを使用してデータを保護してください。
- 受信したペイロードの構造と
event値を検証してください。 - Webhook URLは機密情報として取り扱ってください。
app_idとsecret_keyを設定している場合は、ペイロードのコピーを用いてX-SORI-Signatureヘッダーを検証し、真正性を確認してください。
エラーハンドリング
エンドポイントがイベントを受信できない場合、SORI API Serverは次のように動作します。
- 失敗した配信を 最大3回 再試行します。
- 再試行の間隔には指数バックオフを適用します。
- すべての再試行が失敗した場合はイベントを破棄します。
実装例
Python(FastAPI)
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/webhook")
async def webhook(request: Request):
payload = await request.json()
event = payload.get("event")
if event == "campaign.impression":
# インプレッションイベントの処理
pass
elif event == "campaign.click":
# クリックイベントの処理
pass
elif event == "authentication":
# 認証イベントの処理
pass
return {"status": "success"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=3000)Node.js(Express)
import express from "express";
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
const payload = req.body;
const event = payload.event;
if (event === "campaign.impression") {
// インプレッションイベントの処理
} else if (event === "campaign.click") {
// クリックイベントの処理
} else if (event === "authentication") {
// 認証イベントの処理
}
res.status(200).send({ status: "success" });
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});