コンテンツにスキップ
サポート

Webhook

Webhook を使うと、LabelGrid アカウント内でイベントが発生したときにリアルタイムで通知を受け取れます。Webhook でワークフローを自動化したり、外部システムと連携したりできます。

開発者の方へ: Webhook は API からプログラムで管理することもできます。エンドポイントと例については、LabelGrid API ドキュメントをご覧ください。

  1. Webhook を設定する - URL と、どのイベントを受け取るかを指定します
  2. イベントが発生する - たとえば、リリースがストアに配信されます
  3. LabelGrid が POST リクエストを送信する - あなたのサーバーがイベントデータを受け取ります
  4. システムがそれを処理する - イベントに応じてワークフローを自動化します

  1. 右上のプロフィールアイコンをクリックします
  2. ドロップダウンメニューから Webhooks を選択します

  1. Create Webhook をクリックします
  2. この Webhook を識別するための Name を入力します
  3. 通知を受け取りたい URL を入力します
  4. この Webhook をトリガーする Events を選択します
  5. Create をクリックします

Webhook を作成すると、シークレットキー が発行されます。受信したリクエストが実際に LabelGrid からのものかを検証するために使います。

  • シークレットは安全に保管してください
  • 受信リクエストの署名を検証してください
  • 漏えいした場合は、シークレットを再生成してください

次のイベントを受け取るように Webhook を設定できます。イベント識別子 は、ペイロードの event プロパティと X-Webhook-Event ヘッダーに表示される値です。

イベント識別子説明
delivery.completedリリースが配信先へ正常に配信されたときに発生します
delivery.failed配信先への配信が失敗したときに発生します
takedown.completedテイクダウンのリクエストが完了したときに発生します
release.review.status_changedリリースの審査ステータスが変わったときに発生します
release.distributedリリースが配信されたときに発生します
payment.statement_ready支払い明細が閲覧できる状態になったときに発生します

1 つの Webhook で複数のイベントを選ぶことも、イベントの種類ごとに別々の Webhook を作ることもできます。


Webhook の一覧には、次の情報が表示されます。

説明
Name自分で付けた Webhook の名前
URL通知の送信先
Events設定したイベントの数
Status有効か無効か
Success / Fail配信の成功回数と失敗回数
Last TriggeredWebhook が最後に呼び出された時刻
  1. Webhook の行で Edit の操作をクリックします
  2. 名前、URL、イベントを変更します
  3. Save をクリックします

Webhook を削除せずに、有効・無効を切り替えられます。

  • Active - Webhook が通知を受け取ります
  • Inactive - Webhook は一時停止され、通知は送信されません
  1. Webhook の行で Delete の操作をクリックします
  2. 削除を確定します

本番で Webhook に頼る前に、テストしておきましょう。

  1. Webhook の Test の操作をクリックします
  2. LabelGrid が指定の URL にテスト用ペイロードを送信します
  3. エンドポイントがそれを正しく受信・処理できたかを確認します

Webhook の動作を監視し、問題を調査できます。

  1. Webhook の View Logs の操作をクリックします
  2. すべての Webhook 配信の履歴が表示されます

各ログエントリーには、次の情報が表示されます。

項目説明
Event Typeこの配信をトリガーしたイベント
Response Statusあなたのサーバーが返した HTTP ステータスコード
Durationリクエストにかかった時間
Attempt再試行の回数
Timestamp配信が発生した時刻

イベントが発生すると、LabelGrid は指定の URL に JSON ペイロードを含む POST リクエストを送信します。

{
"event": "delivery.completed",
"timestamp": "2026-05-05T10:00:00+00:00",
"webhook_id": "123",
"data": {
// Event-specific data
}
}

timestamp フィールドは ISO 8601 形式です。webhook_id は設定した Webhook の ID で、X-Webhook-Id ヘッダーと一致します。


data オブジェクトの構造は event の種類によって異なります。以下のフィールド型はすべて、ペイロードにシリアライズされた状態の JSON 型です。

リリースの配信が最終的な成功状態に達したときに、配信先ごとに 1 回発生します。

{
"event": "delivery.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete"
}
}
フィールド説明
distro_queue_idintegerこの配信処理の内部キュー ID
release_idinteger配信されたリリース
release_catstring | nullリリースのカタログ参照番号
outlet_idinteger配信先の ID
outlet_namestring | null配信先の表示名(例: "Spotify"
statusstringこのイベントでは常に "complete"

リリースの配信が最終的な失敗状態に達したときに、配信先ごとに 1 回発生します。ペイロードは delivery.completed と同じで、message フィールドが追加されます。

{
"event": "delivery.failed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "error",
"message": "Outlet rejected the delivery: missing ISRC."
}
}
フィールド説明
statusstringerrorfaultrejectedbatch_exception のいずれか
messagestring | null配信先または配信パイプラインからの失敗理由

テイクダウンのリクエストが成功したときに、配信先ごとに 1 回発生します。delivery.completed と同じ構造に、takedown: true のフラグが追加されます。

{
"event": "takedown.completed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"distro_queue_id": 456,
"release_id": 789,
"release_cat": "ABC123",
"outlet_id": 12,
"outlet_name": "Spotify",
"status": "complete",
"takedown": true
}
}

リリースが distributed(配信済み)の配信状態に移行したときに、リリースごとに 1 回発生します。発生するのは distributed への移行時のみで、すでに配信済みのリリースをその後に保存しても発生しません。

{
"event": "release.distributed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"release_cat": "ABC123",
"release_title": "Summer EP",
"delivery_status": "distributed"
}
}

リリースが審査ステータスの間を移動するたびに発生します。

{
"event": "release.review.status_changed",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"release_id": 789,
"release_cat": "ABC123",
"release_title": "Summer EP",
"previous_status": "to_review",
"new_status": "approved"
}
}
フィールド説明
previous_statusstring変更前のステータス。draftto_reviewapprovedrejectedrequire_changesaudit のいずれか
new_statusstring変更後のステータス。値の範囲は同じ

支払い明細が生成され、閲覧できる状態になったときに発生します。

{
"event": "payment.statement_ready",
"timestamp": "2026-05-18T10:00:00+00:00",
"webhook_id": "123",
"data": {
"payment_request_id": 1024,
"invoice_number": "INV-2026-001",
"period": "2026-04-30",
"amount": 1234.56,
"total_due_usd": 1234.56,
"currency": "USD"
}
}
フィールド説明
payment_request_idinteger内部の支払いリクエスト ID
invoice_numberstring明細の請求書参照番号
periodstring | null対象期間の末日(ISO 8601 の日付形式 YYYY-MM-DD
amountnumbercurrency で表した明細金額
total_due_usdnumberUSD に換算した明細合計
currencystringISO 4217 の通貨コード(既定は USD

すべての Webhook 配信には署名が付与されており、それが実際に LabelGrid から送られたものかを検証できます。イベントを処理する前に、必ず署名を検証してください。

すべての Webhook の POST リクエストには、次のヘッダーが含まれます。

ヘッダー説明
X-Webhook-Signature生のリクエストボディの HMAC-SHA256。小文字の 16 進数で、アルゴリズムの接頭辞は付きません
X-Webhook-Timestamp配信の ISO 8601 タイムスタンプ(ボディ内の timestamp プロパティと同じ値)
X-Webhook-Eventイベント識別子(例: delivery.completed
X-Webhook-Id配信を受け取る Webhook の ID
User-AgentLabelGrid-Webhooks/1.0
Content-Typeapplication/json
  • アルゴリズム: HMAC-SHA256
  • エンコーディング: 小文字の 16 進数
  • 接頭辞: なし。値は sha256=... ではなく、16 進ダイジェストそのものです
  • 署名対象: 生の JSON リクエストボディ全体(ボディ自体にタイムスタンプがプロパティとして含まれるため、タイムスタンプも暗黙的に署名されます)
  1. JSON のパースや変換を行う前に、生のリクエストボディ を読み取ります。パース済みの JSON を再シリアライズすると、バイト列が変わって署名が一致しなくなることがあります。
  2. HMAC-SHA256(raw_body, your_webhook_secret) を計算し、小文字の 16 進ダイジェストを取得します。
  3. 定数時間比較を使って X-Webhook-Signature と照合します。
  4. (推奨)X-Webhook-Timestamp がリプレイ許容時間より古い場合は、リクエストを拒否します。許容時間は 5 分を目安にしてください。
$rawBody = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
if (! hash_equals($expected, $signature)) {
http_response_code(401);
exit('Invalid signature');
}
// Reject deliveries older than 5 minutes
if (abs(time() - strtotime($timestamp)) > 300) {
http_response_code(401);
exit('Stale delivery');
}
$payload = json_decode($rawBody, true);
// ... process the event
http_response_code(200);
const crypto = require('crypto');
// Express: capture raw body BEFORE any JSON middleware
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body; // Buffer
const signature = req.header('X-Webhook-Signature') || '';
const timestamp = req.header('X-Webhook-Timestamp') || '';
const expected = crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex');
const sigBuf = Buffer.from(signature, 'hex');
const expBuf = Buffer.from(expected, 'hex');
if (sigBuf.length !== expBuf.length || !crypto.timingSafeEqual(sigBuf, expBuf)) {
return res.status(401).send('Invalid signature');
}
if (Math.abs(Date.now() - new Date(timestamp).getTime()) > 5 * 60 * 1000) {
return res.status(401).send('Stale delivery');
}
const payload = JSON.parse(rawBody.toString('utf8'));
// ... process the event
res.sendStatus(200);
});
import hmac, hashlib
from datetime import datetime, timezone
raw_body = request.get_data() # Flask: bytes, before any JSON parsing
signature = request.headers.get('X-Webhook-Signature', '')
timestamp = request.headers.get('X-Webhook-Timestamp', '')
expected = hmac.new(
webhook_secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature):
return ('Invalid signature', 401)
delivery_time = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
if abs((datetime.now(timezone.utc) - delivery_time).total_seconds()) > 300:
return ('Stale delivery', 401)
return ('', 200) # process the event, then ack
  • ハッシュ化の前にボディを再シリアライズする。 JSON を自動でパースするフレームワーク(Express の express.json()、Laravel の既定のリクエストボディなど)は、元のバイト列を失います。まず生のボディを取得してください。
  • 定数時間でない比較(=====)を使う。 タイミング攻撃を受ける可能性があります。常に hash_equals(PHP)、crypto.timingSafeEqual(Node)、hmac.compare_digest(Python)、または使用言語の同等のものを使ってください。
  • sha256= の接頭辞を期待する。 ヘッダーの値は接頭辞のない 16 進ダイジェストそのものです。
  • タイムスタンプのチェックを省く。 これを省くと、漏えいした署名がいつまでもリプレイされる恐れがあります。

制限
リクエストのタイムアウト10 秒
最大ペイロードサイズ64 KB
ユーザーあたりの最大 Webhook 数10

エンドポイントが 10 秒以内に応答しない場合、その配信は失敗として扱われ、再試行されます。

エンドポイントが 2xx 以外のステータスを返すかタイムアウトした場合、LabelGrid は指数バックオフで再試行します。

試行再試行までの待機時間
1 → 230 秒
2 → 31 分
3 → 42 分
4 → 54 分
5 → 68 分
6 → 716 分
7 → 832 分
8 → 964 分
9 → 10128 分

各間隔には 0〜30 秒のジッターが含まれます。10 回試行しても成功しない場合(合計で約 4.5 時間が経過)、その配信は恒久的な失敗として記録され、それ以上の再試行は行われません。

Webhook の配信失敗が 累計 100 回 に達すると、その Webhook は自動的に無効化されます。エンドポイントを修正したうえで、Webhook の一覧から手動で再有効化してください。無効化された Webhook を再有効化すると、失敗回数がリセットされます。

信頼性のためのベストプラクティス

Section titled “信頼性のためのベストプラクティス”
  • 2xx レスポンス を素早く(10 秒以内に)返す
  • 確認応答を返したあとで、データを非同期に処理する
  • すべてのリクエストで署名を検証する(Webhook 署名の検証 を参照)
  • Webhook の一覧で失敗回数を監視する
  • イベントの取りこぼしを調査するときは、配信ログを確認する

  • リリースが公開されたら Slack にメッセージを送る
  • 配信が失敗したらチームにメールを送る
  • 社内ダッシュボードを更新する
  • リリースが配信されたらマーケティングキャンペーンを開始する
  • 新しいコンテンツが利用可能になったらウェブサイトを更新する
  • 外部のプロジェクト管理ツールにステータスを同期する
  • 配信失敗を即座にアラートで受け取る
  • 配信の進捗をリアルタイムで追跡する
  • 審査ステータスの変化を監視する

Webhook がイベントを受け取らない

Section titled “Webhook がイベントを受け取らない”
  1. ステータスを確認する - Webhook は Active になっていますか?
  2. URL を確認する - エンドポイントはインターネットからアクセスできますか?
  3. イベントを確認する - 正しいイベントが選択されていますか?
  4. ログを確認する - エラーは記録されていませんか?
  1. エンドポイントを確認する - 200 OK を返していますか?
  2. 応答時間を確認する - タイムアウト内に応答していますか?
  3. エラーメッセージを確認する - 何が失敗していますか?
  4. 手動でテストする - テスト用 Webhook を送ってみる

Webhook シークレットが漏えいした場合:

  1. Webhook の設定で Regenerate Secret をクリックします
  2. 新しいシークレットでアプリケーションを更新します
  3. 古いシークレットはただちに無効になります

Webhook についてご不明な点がありましたら、サポートチームにお問い合わせください

LabelGridはまだお使いではありませんか?

いまお読みいただいた内容は、すべて当社のプラットフォームでご利用いただけます。

LabelGridでできることを見る →