> For the complete documentation index, see [llms.txt](https://documentation.pushly.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://documentation.pushly.com/pushly-ja/platform/ibentowebhook.md).

# イベントWebhook

## イベント Webhook

Event Webhooks は、サブスクライバーのイベントが発生するとリアルタイムの HTTP コールバックを送信します。各ドメインごとに 1 つ以上のエンドポイントを設定して、構造化されたイベントデータを発生時に受け取れます。

### 設定

移動先 **ドメイン設定 → Event Webhooks** webhook エンドポイントを作成、編集、またはアーカイブします。

各 webhook は次を指定します:

* **URL**: 宛先エンドポイント
* **ヘッダー**: カスタムヘッダー。シークレット用の Liquid マクロを任意で使用できます
* **イベント**: 購読する 1 つ以上のイベントタイプ

すべての配信は POST で、完全なイベントペイロードを含む JSON 本文です。

<figure><img src="https://810756845-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lry9Z27iWOZyQEGAgY7%2Fuploads%2FcPE6zmhV6y3IDxn1Vrkk%2FScreenshot%202026-05-13%20at%208.02.32%E2%80%AFAM.png?alt=media&#x26;token=b63582bb-7a22-477a-bd94-e4b57fdbbbc8" alt=""><figcaption></figcaption></figure>

### サポートされるイベント

| グループ   | イベント                        | 説明                     |
| ------ | --------------------------- | ---------------------- |
| 通知     | `notification.displayed`    | サブスクライバーのデバイスに通知が表示される |
| 通知     | `notification.clicked`      | サブスクライバーが通知をクリックする     |
| プッシュ購読 | `push_subscription.created` | デバイスがプッシュで到達可能になる      |
| プッシュ購読 | `push_subscription.deleted` | デバイスがプッシュで到達不可能になる     |

**通知イベントはアクションごとに発火します。** 各イベントは、1 アクションにつきサブスクライバーごとに 1 回発火します。10,000 人のサブスクライバーに送信された通知は、最大 10,000 件の個別イベントを生成します。

**プッシュ購読イベントは変更時に発火します。** すでに購読解除されているデバイスに別の購読解除が行われても、何も変化しないためイベントは発生しません。

#### 購読はデバイスごとです

プッシュ購読は人ではなく 1 台のデバイスです。スマートフォンとノートパソコンの両方で到達可能な人は 2 つの購読を持ち、2 件のイベントを生成します。

#### プッシュ購読イベントは有効化した時点で開始します

これらのイベントを購読しても、過去の履歴が再送されたり、オーディエンスの現在の状態が送信されたりはしません。購読が保存された瞬間からの変更のみを受け取るため、これらのイベントだけで構築した到達可能性は、その時点以降に変更されたデバイスに対してのみ完全です。オーディエンスの初期状態が必要な場合は、別途用意してください。

### ペイロード構造

イベント固有のデータを含む固定エンベロープです。null フィールドは省略されます。

{% tabs %}
{% tab title="通知イベント" %}
{% code expandable="true" %}

```javascript
{
  "version": 1,
  "event_type": "notification.displayed",
  "event_id": "d426d74a-b0cc-46e3-9a6d-d12ac346effe",
  "event_timestamp": "2026-05-08T14:17:30.452Z",
  "domain": {
    "id": 1234,
    "name": "Cafe 80s",
    "url": "cafe80s.com"
  },
  "user": {
    "id": "atk3i6E0NtohVy4UlvvkGQfl6J5wNTYp",
    "external_id": "11298b50-c1a7-4db3-aebc-829fed8b0d94",
    "device": {
      "platform": "web",
      "type": "desktop",
      "os": "macos",
      "os_version": "14.4",
      "browser": "chrome",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
      "time_zone": "America/Chicago",
      "location": {
        "city": "Hill Valley",
        "province": "California",
        "country_code": "US",
        "postal_code": "19851",
        "continent_code": "NA"
      }
    }
  },
  "data": {
    "notification": {
      "id": 21015,
      "title": "時計台を救え！",
      "body": "ウィルソン市長の保存協会があなたの助けを必要としています — 雷が落ちる前に寄付してください",
      "keywords": ["hill-valley", "preservation", "clock-tower"],
      "send_timestamp": "2026-05-08T14:17:25.000Z",
      "source": "manual",
      "delivery_type": "scheduled",
      "segment_ids": [1955, 1985],
      "segment_names": ["ヒル・バレー住民", "保存協会メンバー"],
      "campaign": {
        "id": 88,
        "step_id": 1
      }
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="プッシュ購読イベント" %}
作成イベントには `subscribed_url` も含まれず `reason_code`.

{% code expandable="true" %}

```javascript
{
  "version": 1,
  "event_type": "push_subscription.created",
  "event_id": "01a05848-62b0-77d9-add4-af0567c92290",
  "event_timestamp": "2026-08-31T14:45:34.000Z",
  "domain": {
    "id": 1234,
    "name": "Cafe 80s",
    "url": "cafe80s.com"
  },
  "user": {
    "id": "u7CGRJJoGjejItgAEIV3X0uadD2byP81",
    "external_id": "11298b50-c1a7-4db3-aebc-829fed8b0d94",
    "device": {
      "platform": "web",
      "type": "mobile",
      "os": "android",
      "os_version": "11",
      "browser": "chrome",
      "time_zone": "America/Chicago",
      "location": {
        "city": "Hill Valley",
        "province": "California",
        "country_code": "US",
        "postal_code": "19851",
        "continent_code": "NA"
      }
    }
  },
  "data": {
    "push_subscription": {
      "subscribed_url": "https://cafe80s.com/menu"
    }
  }
}
```

{% endcode %}

削除イベントには `reason_code` も含まれず `subscribed_url`.

{% code expandable="true" %}

```javascript
{
  "version": 1,
  "event_type": "push_subscription.deleted",
  "event_id": "01a05849-1f30-7c41-9b02-3ad7715c8f19",
  "event_timestamp": "2026-08-31T15:02:11.000Z",
  "domain": {
    "id": 1234,
    "name": "Cafe 80s",
    "url": "cafe80s.com"
  },
  "user": {
    "id": "u7CGRJJoGjejItgAEIV3X0uadD2byP81",
    "external_id": "11298b50-c1a7-4db3-aebc-829fed8b0d94",
    "device": {
      "platform": "web",
      "type": "mobile",
      "os": "android",
      "os_version": "11",
      "browser": "chrome",
      "time_zone": "America/Chicago"
    }
  },
  "data": {
    "push_subscription": {
      "reason_code": "user_unsubscribed"
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### エンベロープ

| フィールド             | 型                    | 説明                                              |
| ----------------- | -------------------- | ----------------------------------------------- |
| `version`         | int                  | ペイロードスキーマのバージョン                                 |
| `event_type`      | string               | ドット区切りの名前空間付きイベント識別子（例: `notification.clicked`) |
| `event_id`        | string（UUID）         | このイベントの安定識別子です。重複排除キーとして使用します。                  |
| `event_timestamp` | string（ISO 8601、UTC） | イベントが発生した時刻                                     |
| `domain`          | object               | このイベントが属する Pushly ドメイン                          |
| `user`            | object               | このイベントを発生させたサブスクライバー                            |
| `data`            | object               | イベント固有の内容。イベントの subject に一致するキーが 1 つだけ含まれます。    |

その `data` オブジェクトにはイベントの subject に一致する単一のキーが含まれます。例えば、 `notification.*` イベントでは、そのキーは `notification`; 一方 `push_subscription.*` イベントでは、 `push_subscription`.

その `user` と `device` ブロックは、すべてのイベントタイプで形も値も同一なので、どのイベントに含まれていたかに関係なく、それらをキーにできます。 `device.user_agent` は通知イベントにのみ含まれます。

すべてのタイムスタンプは、ミリ秒精度の ISO 8601 UTC 形式です。

#### プッシュ購読データ

| フィールド            | 型      | 含まれるのは    | 説明                             |
| ---------------- | ------ | --------- | ------------------------------ |
| `subscribed_url` | string | `created` | デバイスが購読したページ。判明している場合          |
| `reason_code`    | string | `deleted` | デバイスが到達不可能になった理由。下の表を参照してください。 |

#### 理由コード

削除には必ず `reason_code`が含まれます。対処すべき区別は、その人が意思決定をしたのか、それとも配信が単に動かなくなったのかです。最初の 2 つは本人の選択、次の 2 つはそうではありません。

| 理由コード                   | 意味                                                                        |
| ----------------------- | ------------------------------------------------------------------------- |
| `user_unsubscribed`     | 本人がオプトアウトしました。あなたのアプリやサイト内の操作で、またはブラウザーもしくは OS から通知許可を取り消したことによります。       |
| `data_deletion_request` | ユーザーデータ削除リクエストの一環としてデバイスが削除されました。                                         |
| `token_invalidated`     | プッシュプロバイダーがデバイストークンを無効と判断して拒否しました。本人による操作はなく、同じデバイスから再度購読することもできます。       |
| `scrub`                 | 一定期間エンゲージメントがなかったため、リストの整理によってデバイスが削除されました。本人による操作はなく、トークンはまだ有効な可能性があります。 |
| `consolidated`          | デバイスのレコードが同一人物の別レコードに統合されました。 **なお到達可能です**、存続しているレコードの下で。下の注記を参照してください。   |
| `other`                 | 上記のいずれにも該当しない内部理由です。                                                      |

{% hint style="warning" %}
**`consolidated` その人が通知の受信をやめたことを意味するわけではありません。** ほかの理由コードはすべてそうです。これは、Pushly が 2 つのレコードを同一人物として認識し、それらを統合したことを意味します。本人は存続しているレコードの下で引き続き到達可能であり、そのレコードには別の `user.id`が付与されます。オプトアウトではなく識別子の変更として扱ってください。これで抑止すると、まだ購読中の人を削除してしまいます。
{% endhint %}

`user_unsubscribed` 自社インターフェースでのオプトアウトと、ブラウザーまたは OS レベルで取り消された権限の両方を含みます。この 2 つは互いに区別できません。

1 つの `push_subscription.created` イベントには含まれません `reason_code`。初回購読とデバイスが再び到達可能になった場合は、どちらも作成として報告され、区別されません。

見覚えのないものは、 `reason_code` 次と同じように扱ってください `other` — 参照: [バージョニング](#versioning).

### ヘッダーマクロ

カスタムヘッダー値では、保存されたドメインシークレットを参照するための Liquid テンプレート構文をサポートしています:

```
Authorization: Bearer {{ secret.analytics_token }}
```

ヘッダーテンプレートで明示的に参照されたシークレットのみが読み込まれます。システム管理のシークレット（先頭が `_`）のエイリアス）はテンプレートからアクセスできません。

### ヘッダー

すべての配信には次が含まれます:

| ヘッダー                          | 説明                             |
| ----------------------------- | ------------------------------ |
| `X-Pushly-Webhook-Id`         | この配信を生成した webhook 設定を識別する UUID |
| `X-Pushly-Event-Id`           | ペイロード内の一意のイベント ID。重複排除に使用します。  |
| `X-Pushly-Signature`          | リクエスト本文の HMAC-SHA256 署名（下記参照）  |
| `X-Pushly-Delivery-Timestamp` | 配信が開始された時刻の Unix タイムスタンプ（秒）    |
| `X-Pushly-Attempt-Number`     | 1 始まりの試行番号                     |
| `Content-Type`                | `application/json`             |

予約済みヘッダー名（`Content-Type`, `Host`, `Content-Length`, `Transfer-Encoding`、先頭が `X-Pushly-`）は、webhook 設定のカスタムヘッダーで上書きできません。

### 署名検証

すべての配信には `X-Pushly-Signature` 真正性検証用のヘッダーが含まれます。

形式: `sha256=<hex_digest>`

検証アルゴリズム:

1. 署名シークレットを UTF-8 バイト列としてエンコードします
2. シークレットをキーとして、元のリクエスト本文に対して HMAC-SHA256 を計算します
3. ダイジェストを16進エンコードします
4. の後ろの値と比較します `sha256=` ヘッダー内の値を一定時間比較で

**重要:** 解析して再シリアル化したものではなく、元のリクエスト本文に対して検証してください。JSON のキー順序と空白はダイジェストに影響します。

**リプレイ防止（任意）:** 比較する `X-Pushly-Delivery-Timestamp` をサーバーの現在時刻と比較します。許容範囲（例: 5 分）より古い配信は拒否してください。

署名シークレットは次で確認できます **ドメイン設定 → Event Webhooks → Signing Secret**.

### 冪等性

Pushly は 1 アクションにつきサブスクライバーごとに 1 件のイベントを生成します。エンドポイントが失敗またはタイムアウトした場合、イベントは複数回配信されることがあります。そのため、 `event_id` 重複排除に使用し、確認応答後に非同期で処理する場合は、そちら側で永続キューを使ってください。

1 つの `event_id` は特定の変更を識別し、その再配信の間も同じ値のままです。

### 配信

#### タイムアウト

エンドポイントは 5 秒以内に応答しなければなりません。処理にそれ以上かかる場合は、すぐに応答して非同期で処理してください。

#### 再試行

| 試行 | 遅延             |
| -- | -------------- |
| 1  | 即時             |
| 2  | 1 回目の失敗の 4 秒後  |
| 3  | 2 回目の失敗の 16 秒後 |

3 回の試行後、イベントはデッドレターキューに移動します。

再試行される: HTTP 5xx、タイムアウト、接続エラー。再試行されない: HTTP 4xx、SSRF ブロック。

#### 順序

イベントは順不同で到着する場合があります。 `event_timestamp` を時系列順に並べるためであり、配信順ではありません。

これは、同じデバイスの購読と購読解除が一連の流れを表すプッシュ購読イベントで特に重要です。 `event_timestamp` は秒単位の精度なので、同じ秒内に購読解除して再購読したデバイスは、ペイロードだけでは順序を判断できません。

### ネットワークセキュリティ

webhook 配信の主要な認証手段は HMAC 署名検証（上記）です。すべての配信には `X-Pushly-Signature` 署名シークレットを使ってリクエスト本文と照合できるヘッダーが含まれます。これは、リクエストが Pushly から来たことを示す暗号学的な証明であり、ネットワークの出所に依存せず、こちら側のインフラ変更にも耐えます。

#### IP 許可リスト

送信元 IP の固定リストは公開していません。セキュリティまたはコンプライアンス方針で IP ベースのネットワーク制御が必要な場合は、Pushly のアカウントマネージャーに連絡して要件をご相談ください。

#### TLS

webhook エンドポイントは HTTPS 経由で到達可能でなければなりません。Pushly は平文 HTTP の URL には配信しません。TLS 1.2 以上を要求し、標準の公開証明書認証局に対してエンドポイントの証明書を検証します。自己署名証明書はサポートされません。

#### SSRF 保護

Pushly は接続前に、すべての宛先 URL をプライベートおよび予約済み IP 範囲と照合して検証します。プライベート（RFC 1918）、ループバック、リンクローカル、インスタンスメタデータのアドレスへのリクエストは拒否されます。

### バージョニング

新しいフィールドとイベントタイプは、増分なしで追加されます `version`。ハンドラーでは未知のフィールドを無視してください。

破壊的変更（フィールドの削除や名前変更）は、増分します `version` 非推奨期間を伴って。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://documentation.pushly.com/pushly-ja/platform/ibentowebhook.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
