プッシュ候補
非同期 API を通じて、ユーザーごとにパーソナライズされたプッシュ通知候補を生成します。
概要
POST /v1/push-candidatesでジョブを送信します。- API が候補を非同期で処理し、
job_idを返します。 GET /v1/push-candidates/{job_id}でステータスをポーリングするか、完了時に Webhook を受信します。- 署名付き URL から JSONL 結果ファイルをダウンロードします。
ジョブの作成
curl -X POST "https://api.feed.storyhub.studio/v1/push-candidates" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scenario": "push_morning",
"candidates_per_user": 3
}'
candidates_per_user は 1 ユーザーあたりに生成される候補記事数です(デフォルト: 1、最大: 3)。
レスポンス:
{
"job_id": "job-uuid",
"status": "queued"
}
ジョブステータスの確認
GET /v1/push-candidates/{job_id}
| ステータス | 意味 |
|---|---|
queued | ジョブはキューに入っています |
processing | ジョブは実行中です |
completed | 結果の準備が完了しました — download_url が提供されます |
failed | ジョブが失敗しました — error フィールドを確認してください |
完了すると、レスポンスに download_url(署名付き URL、有効期限 1 時間)が含まれます。
ステータスレスポンスの主要フィールド
| フィールド | 型 | 出現条件 | 説明 |
|---|---|---|---|
target_user_count | integer | null | オーディエンス解決後 | ジョブの対象ユーザー数。queued や解決前の processing / failed では未設定(null またはフィールド省略)。condition モードで合致 0 人の場合は 0(null と区別されます)。 |
failed_user_count | integer | null | status=completed のみ確定値 | 対象ユーザーのうち候補生成がエラーで失敗したユーザー件数。queued / processing / failed では未設定。0 人完了時も 0 で null と区別されます。 |
candidates_generated | integer | null | status=completed のみ確定値 | 全ユーザー合算の生成候補数。queued / processing / failed では未設定(null またはフィールド省略)— ここで「候補生成に到達しなかった」ことを 0(合致ユーザーなしで完了)と区別できます。 |
consumed_credits | number | 常に存在(完了前は 0) | 確定したクレジット消費量。 |
filter_stats | object | null | target_mode=condition 完了時 | オーディエンスフィルタの内訳(後述)。target_mode=manual では null。 |
download_url | string | null | status=completed | JSONL のダウンロード URL(署名付き、有効期限あり)。 |
これらのフィールドは Webhook ペイロード(push_job.completed)と同じ値で、GET と Webhook の対称性が保たれます。
完了時のレスポンス例:
{
"job_id": "01HZX9...",
"status": "completed",
"progress": 100,
"target_user_count": 1234,
"failed_user_count": 3,
"candidates_generated": 3702,
"consumed_credits": 12.34,
"download_url": "https://storage.googleapis.com/...",
"download_url_expires_at": "2026-04-27T10:00:00Z",
"filter_stats": {
"total_audience": 5000,
"excluded_due_to_null_last_active": 200,
"excluded_due_to_last_active_days": 3500,
"excluded_due_to_registered_range": 66
},
"error": null
}
target_mode=condition で条件解決の結果、対象ユーザーが 0 人になった場合も、ジョブは completed で完了します(failed にはなりません)。
status=completed、target_user_count=0、failed_user_count=0、candidates_generated=0、consumed_credits=0- 予約クレジットは解放されます(消費は 0)
- 空の JSONL(0 行)に対する
download_urlが発行されます - 通常通り
push_job.completedの Webhook が送信されます
filter_stats を確認することで、なぜ対象が 0 人になったか(last_active_days 除外など)を把握できます。
download_url は認証情報が URL のクエリパラメーターに含まれた署名付き URL です。追加の認証ヘッダーは不要で、そのまま HTTP GET でダウンロードできます。
curl -o candidates.jsonl "DOWNLOAD_URL_FROM_RESPONSE"
有効期限が切れた場合は、GET /v1/push-candidates/{job_id} を再度呼び出すと新しい URL が発行されます。
結果フォーマット(JSONL)
ダウンロードしたファイルの各行は JSON オブジェクトです。candidates 配列はスコア降順(先頭が最もスコアの高い記事)で並びます:
{"user_id": "user-123", "candidates": [{"id": "content-uuid", "title": "...", "url": "...", "thumbnail": {"url": "...", "width": 1024, "height": 512}, "source": {"name": "..."}, "published_at": "2025-01-15T08:00:00Z", "score": 0.95, "tracking_token": "eyJ..."}]}
プッシュクリックのトラッキング
プッシュ記事のクリックを報告する際は、JSONL 出力の tracking_token を含めてください:
POST /v1/events
{
"events": [
{
"type": "click",
"session_id": "...",
"tracking_token": "eyJ... (from push candidates JSONL)",
"occurred_at": "..."
}
]
}
プッシュクリックをトラッキングするメリット:
- プッシュ経由でクリックされた記事は、以降のフィードリクエストから除外されます(重複排除)。
- クリックシグナルにより、将来のレコメンデーションが改善されます。
- アナリティクスでプッシュ通知のエンゲージメントを測定できます。
tracking_token についてtracking_token はフィード取得(GET /v1/feed)と Push 候補ファイル(JSONL)に含まれます。一方、GET /v1/contents/{content_id}(単体取得)にはランキング文脈がないため、tracking_token は含まれません。
Webhooks
テナントに webhook_url が設定されている場合、ジョブ完了時に通知が送信されます:
completedまたはfailedステータス時に配信されます。- 検証用の
X-StoryHub-Signatureヘッダー(HMAC-SHA256)が含まれます。 - ベストエフォート配信で、最大 3 回リトライされます。
Webhook ペイロード
{
"event": "push_job.completed",
"job_id": "...",
"tenant_id": "...",
"scenario_id": "...",
"status": "completed",
"target_user_count": 1234,
"failed_user_count": 3,
"candidates_generated": 3702,
"consumed_credits": 12.34,
"download_url": "https://...",
"download_url_expires_at": "...",
"filter_stats": {
"total_audience": 5000,
"excluded_due_to_null_last_active": 200,
"excluded_due_to_last_active_days": 3500,
"excluded_due_to_registered_range": 66
},
"completed_at": "..."
}
filter_stats(target_mode=condition のみ)
target_mode=condition の場合、filter_stats でフィルタの内訳を確認できます。各カウンタは独立しており、複数の条件で除外されたユーザーはそれぞれのカウンタに重複してカウントされます。
| フィールド | 説明 |
|---|---|
total_audience | テナントの総オーディエンス数(フィルタ前) |
excluded_due_to_null_last_active | last_active_days が指定されているが last_active_at が NULL のため除外された数 |
excluded_due_to_last_active_days | last_active_at が cutoff(now − last_active_days)より古いため除外された数 |
excluded_due_to_registered_range | registered_from / registered_to の範囲外のため除外された数 |
target_mode=manual の場合、filter_stats は null です。同じ値が GET /v1/push-candidates/{job_id} のレスポンスにも含まれます。
target_user_count / failed_user_count / candidates_generated も Webhook と GET レスポンスの両方で公開されており、両系統での集計を一致させることができます。failed_user_count は候補生成がエラーで失敗したユーザー数を表します(成功して候補 0 件の場合は失敗扱いになりません)。
制約
| 制限 | 値 |
|---|---|
| テナントあたりの最大同時実行ジョブ数 | 5 |
| 実行時間(10 万ユーザー) | 10 分未満 |
| ダウンロード URL の有効期限 | 1 時間 |
| リクエストボディサイズ | 8 MiB |
user_ids の最大件数(manual モード) | 100,000 件 |
1 件あたりの user_id の長さ | 128 文字以下 |
| 専用レート制限 | 1 RPS / burst 5 |
user_ids 件数や本文サイズが上限を超える場合のレスポンス:
- 件数超過 →
400 Validation(user_ids: must not exceed 100000 entries) - ボディサイズ超過 →
413 Payload Too Large
1 件あたりの user_id が長い場合(例えば 128 文字に近い長さ)、件数上限に達する前にボディサイズ上限 8 MiB に当たる可能性があります。100,000 件を超えるユーザーへ配信したい場合は、リクエストを複数バッチに分割するか、target_mode=condition の活用を検討してください。
condition モード(推奨)
ユーザー絞り込み条件で対象を選びます。
POST /v1/push-candidates
{
"scenario": "push_morning",
"target_mode": "condition",
"conditions": { "last_active_days": 7 }
}
| 条件 | 説明 |
|---|---|
last_active_days | 指定日数以内に session_start 等のイベントがあったユーザーのみを対象とします。 |
last_active_days(NULL の挙動) | last_active_at が未設定(一度もイベント送信が無い)ユーザーは、本フィルタ適用時は対象外となります。 |
last_active_days(未指定時) | 本フィールドを省略するとフィルタは無効化され、活動記録の無いユーザーも含めて全員が対象となります。 |
registered_from / registered_to | registered_at(PUT /v1/users で送信した値、または初回イベント時刻)の範囲で絞り込みます。 |
復帰促進など、まだ一度もアクセスしていないユーザーを含めたい場合は、last_active_days を省略してください。条件を省略すると「絞り込みなし=全ユーザー」となります。
テスト用: manual モード
特定のユーザーを指定して候補を生成できます。開発・テスト時に便利です。
POST /v1/push-candidates
{
"scenario": "YOUR_PUSH_SCENARIO",
"target_mode": "manual",
"user_ids": ["user-12345", "user-67890"]
}
user_ids の各エントリは、/v1/events の user_id と同じ文字種ルール(A-Za-z0-9._:@|+=-、最大 128 文字)に従う必要があります。違反すると 400 Validation を返します。
クレジット
プッシュ候補の生成は、候補が生成されたユーザーごとにクレジットを消費します。詳細は クレジット使用量 をご覧ください。
候補生成に失敗したユーザー(JSONL 上で candidates が空配列となる行、Webhook ペイロードの failed_user_count に集計される行)はクレジット消費の対象外です。確定消費量は consumed_credits フィールドおよび Webhook ペイロードで確認できます。