# API リファレンス

> Webhook Admin の公開 API（/v1）の一覧。認証・エラー・回数制限・各エンドポイントの入力と返り。

Source: https://webhookadmin.com/ja/docs/api/

## 基本

- **URL**: `https://api.webhookadmin.com/v1`
- **認証**: `Authorization: Bearer sk_live_…` か `sk_test_…`。キーの環境の中だけを読み書きします
- **形式**: JSON。時刻は Unix ミリ秒、ID は接頭辞付きの文字列
- **本文の上限**: 1.1MB（超えると 413 `too_large`）
- **一覧**: `?cursor=&limit=`（`limit` は 1〜100、既定 50）。返りは `{ items, next_cursor }`

一覧の `cursor` とメッセージ・送信の ID は、保存期間の最古の月から今月までのものだけ受け付けます（外れた `cursor` は 422、ID は 404）。

## エラー

```
{ "error": "<code>", "message": "<説明>" }
```

| HTTP | `error` | 内容 |
| --- | --- | --- |
| 401 | `unauthorized` | キーが無い・正しくない・取り消し済み・期限切れ |
| 402 | `plan_limit` | プランの上限 |
| 403 | `forbidden` | キーの権限に無い操作 |
| 404 | `not_found` | 見つからない |
| 409 | `conflict` | ほかの内容とぶつかる |
| 413 | `too_large` | 本文が 1.1MB を超える |
| 422 | `invalid` | 入力の誤り |
| 429 | `rate_limited` | 回数制限を超えた |

## 回数制限

1 分あたりの回数を、環境ごとに全部のキーを合わせて数えます。

| プラン | 1 分あたり |
| --- | --- |
| Free | 600 |
| Starter | 3,000 |
| Pro | 12,000 |
| Business | 20,000 |

| 応答ヘッダー | 内容 |
| --- | --- |
| `x-ratelimit-limit` | 1 分あたりの回数 |
| `x-ratelimit-remaining` | この 1 分の残り |
| `x-ratelimit-reset` | この 1 分が終わる時刻（Unix 秒） |
| `retry-after` | 429 のときだけ。送り直してよいまでの秒数 |

## エンドポイント

| メソッド | パス | 権限 |
| --- | --- | --- |
| POST | [`/v1/messages`](#send-message) | `messages:send` |
| GET | [`/v1/messages`](#list-messages) | `logs:read` |
| GET | [`/v1/messages/{id}`](#get-message) | `logs:read` |
| POST | [`/v1/deliveries/{id}/retry`](#retry-delivery) | `messages:retry` |
| GET | [`/v1/consumers`](#list-consumers) | `logs:read` |
| POST | [`/v1/consumers`](#create-consumer) | `consumers:write` |
| POST | [`/v1/consumers/{id}/portal`](#create-portal-link) | `endpoints:write` |
| GET | [`/v1/endpoints`](#list-endpoints) | `logs:read` |
| POST | [`/v1/endpoints`](#create-endpoint) | `endpoints:write` |
| GET | [`/v1/endpoints/{id}`](#get-endpoint) | `logs:read` |
| PATCH | [`/v1/endpoints/{id}`](#update-endpoint) | `endpoints:write` |
| DELETE | [`/v1/endpoints/{id}`](#delete-endpoint) | `endpoints:write` |
| POST | [`/v1/endpoints/{id}/rotate-secret`](#rotate-secret) | `endpoints:write` |
| POST | [`/v1/endpoints/{id}/recover`](#recover-endpoint) | `messages:retry` |
| GET | [`/v1/endpoints/{id}/recoveries/{recovery_id}`](#get-recovery) | `logs:read` |
| GET | [`/v1/event-types`](#list-event-types) | 不要（環境のキーならどれでも） |
| POST | [`/v1/event-types`](#create-event-type) | `endpoints:write` |

## メッセージの送信

```
POST /v1/messages
```

権限: `messages:send`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `consumer` | string | 顧客の `external_id`（200 文字まで） |
| `event_type` | string | イベントの種類。英数字と `_ . : -`、100 文字まで |
| `payload` | JSON | 送る本文。1MB まで |
| `Idempotency-Key`（ヘッダー） | string | 任意。1〜256 文字 |

返り: 202 `{ id, deliveries }`。`deliveries` は作った送信の数。

- 同じ `Idempotency-Key` は 24 時間のあいだ同じ `id` を返します（本文が違っても最初のもの）。
- `consumer` の顧客が無ければ、その場で作ります（送信先が無いので `deliveries` は 0）。
- 応答ヘッダー `x-usage-month` に、組織の今月（UTC）の本番の送信数が入ります。
- テスト環境の送信と試しの送信は料金の対象外で、1 つの環境で 1 日（UTC）1,000 通までです。超えると 402 `plan_limit`。

## メッセージの一覧

```
GET /v1/messages
```

権限: `logs:read`

クエリ: `status`（`pending` / `retrying` / `success` / `failed`）・`event_type`・`q`・`since`（Unix ミリ秒。この時刻以降に作られたもの）・`cursor`・`limit`。

`q` はメッセージの ID・イベントの種類・顧客の `external_id`・`Idempotency-Key` の部分一致です。本文の中身は探しません。

`q`・`status`・`event_type` を付けて `since` を省くと、直近 7 日に作られたメッセージだけを返します。

返り: `{ items: MessageRow[], next_cursor }`。

## メッセージの詳細

```
GET /v1/messages/{id}
```

権限: `logs:read`

返り: `MessageDetail`（本文と、送信先ごとの送信・試行）。

## 送信のやり直し

```
POST /v1/deliveries/{id}/retry
```

権限: `messages:retry`

`id` は `Delivery` の ID。返り: 202。

## 顧客の一覧

```
GET /v1/consumers
```

権限: `logs:read`

クエリ: `cursor`・`limit`。返り: `{ items: Consumer[], next_cursor }`。

## 顧客の作成

```
POST /v1/consumers
```

権限: `consumers:write`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `external_id` | string | 送る側のサービスでの顧客の ID（200 文字まで） |
| `name` | string | 任意。200 文字まで |

返り: 201 `Consumer`。同じ `external_id` の顧客があるときは 409 `conflict`。

## 顧客向けの設定画面のリンクの作成

```
POST /v1/consumers/{id}/portal
```

権限: `endpoints:write`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `frame_origin` | string | 任意。iframe で埋め込む親のページのオリジン（`https://app.example.com`。パスは付けない） |
| `locale` | `ja`・`en` | 任意。画面の言語。省くとブラウザの言語 |

返り: 201 `{ url, expires_at }`。`url` は 15 分で切れ、期限までは何度でも開けます。Free のプランは 402 `plan_limit`、ほかの環境の顧客は 404。

自社の画面への埋め込み（`src` は返ってきた `url`）:

**HTML**

```
<iframe
  src="https://app.webhookadmin.com/portal/eyJlIjoi…?lang=ja"
  title="Webhook"
  allow="clipboard-write"
  style="display: block; width: 100%; height: 900px; border: 0"
></iframe>
```

- 埋め込めるのは `frame_origin` のオリジンだけです。`frame_origin` の無いリンクは iframe の中に表示されません（`Content-Security-Policy: frame-ancestors`）。
- 画面はクッキーを使わず、リンクのトークンで動きます。別のドメインの iframe（Safari を含む）の中でも使えます。
- 顧客ができること: 送信先の登録・編集・一時停止・削除、受け取るイベントの選択、署名の鍵の表示と切り替え、試しの送信、配信記録と再送。
- `url` にクエリ `theme=dark` を足すと暗い配色になります。

## 送信先の一覧

```
GET /v1/endpoints
```

権限: `logs:read`

クエリ: `consumer_id`（任意）。返り: `{ items: Endpoint[] }`。

## 送信先の作成

```
POST /v1/endpoints
```

権限: `endpoints:write`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `consumer_id` | string | 顧客の `id`（`con_…`） |
| `url` | string | 送信先の URL（2,000 文字まで） |
| `event_types` | string[] | 任意。受け取るイベントの種類。省くとすべて |
| `fixed_ip` | boolean | 任意。固定の送信元 IP から送る |
| `description` | string | 任意。200 文字まで |
| `retry` | object | 任意。再送の回数と間隔（[再送の設定](#retry-policy)）。省くと既定 |
| `compat_signature` | object | 任意。互換の署名（[署名検証](https://webhookadmin.com/ja/docs/sdk/#compat-signature)） |

返り: 201 `Endpoint` と `secret`（署名の鍵。このときだけ）。

- URL は https（ポート 443 か 8443）。
- IP アドレスを直接書いた URL・社内向けの名前・社内アドレスを指す名前・名前が引けないものは 422。
- 1 つの顧客に登録できる送信先は 20 までです。超えると 422。

## 送信先の詳細

```
GET /v1/endpoints/{id}
```

権限: `logs:read`

返り: `Endpoint` と `recent: AttemptRow[]`・`retrying_count`・`recovery`（最後のまとめて再送。無ければ `null`）。

## 送信先の変更

```
PATCH /v1/endpoints/{id}
```

権限: `endpoints:write`

入力（すべて任意）: `url`・`event_types`・`status`（`active` / `paused`）・`description`・`retry`・`compat_signature`。返り: `Endpoint`。

- `paused` にした送信先あての送信は保留になり、`active` に戻すと今月と先月の保留分をまとめて送ります。
- 自動で止まった送信先（`disabled`）は、`status` を `active` にすると戻ります。
- `retry` は `null` で既定に戻り、`compat_signature` は `null` で外れます。

## 送信先の削除

```
DELETE /v1/endpoints/{id}
```

権限: `endpoints:write`

返り: 204。

## 署名の鍵の切り替え

```
POST /v1/endpoints/{id}/rotate-secret
```

権限: `endpoints:write`

返り: `{ secret }`。古い鍵の署名も 24 時間は付けて送ります。

## まとめて再送

```
POST /v1/endpoints/{id}/recover
```

権限: `messages:retry`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `since` | number | Unix ミリ秒。この時刻以降に作られたメッセージが対象。保存期間の中で、今より前 |

返り: 202 `Recovery`。進み具合は [まとめて再送の状態](#get-recovery) で確かめます。

- この送信先に届かずに失敗で終わった送信を、同じメッセージ（同じ `webhook-id`）のまま送り直します（[まとめて再送](#recovery)）。
- 送信先が `active` でないときは 409 `conflict`。再開してから呼びます。
- 1 つの送信先で動けるのは 1 つだけです。動いているあいだは 409 `conflict`。

## まとめて再送の状態

```
GET /v1/endpoints/{id}/recoveries/{recovery_id}
```

権限: `logs:read`

返り: `Recovery`。`status` は `running`（送っている途中）・`done`（終わった）・`stopped`（途中で送信先が止まった・削除された）。

## イベントの種類の一覧

```
GET /v1/event-types
```

権限: 不要（環境のキーならどれでも）

返り: `{ items: EventType[] }`（名前の順。ページ送りはありません）。

## イベントの種類の登録

```
POST /v1/event-types
```

権限: `endpoints:write`

| 入力 | 型 | 内容 |
| --- | --- | --- |
| `name` | string | イベントの種類。英数字と `_ . : -`、100 文字まで |
| `description` | string | 任意。200 文字まで |

返り: 201 `EventType`。同じ `name` の種類があるときは 409 `conflict`。

登録した種類は、管理画面と顧客向けの設定画面で送信先の種類を選ぶときの候補になります。

## 再送の設定

再送の回数と間隔は送信先ごとに選べます。管理画面・API（`retry`）・MCP・顧客向けの設定画面で設定します。

| 項目 | 選べる値 |
| --- | --- |
| `count` | `0`・`1`・`2`・`3`・`5`・`7`・`12`（最初の送信の後に送り直す回数。`0` は再送しない） |
| `interval` | `progressive`（5 秒・5 分・30 分・2 時間・5 時間・10 時間・10 時間、その後は 10 時間ごと）、`1m`・`5m`・`30m`・`1h`・`6h`（一定の間隔） |

```
{ "retry": { "count": 3, "interval": "5m" } }
```

- 既定は `count: 7`・`interval: progressive` です（最初の送信を含めて最大 8 回、約 28 時間）。`retry: null` で既定に戻ります。
- 最初の送信から最後の再送までが 3 日を超える組み合わせは選べません（422）。当たるのは `progressive` と `12` の組み合わせです。
- 失敗が 5 日続いた送信先の自動停止は、再送の設定とは別に数えます。

## まとめて再送

送信先の障害が直ったあと、その送信先に届かずに失敗で終わった送信を、指定した時刻以降の分だけまとめて送り直します。同じメッセージ（同じ `webhook-id`）のまま送るので、受け側は処理済みの ID を捨てられます。

- **管理画面**: 送信先の詳細
- **API**: `POST /v1/endpoints/{id}/recover`（[入力と返り](#recover-endpoint)）
- **MCP**: `recover_endpoint`・`get_recovery`
- **顧客向けの設定画面**: 顧客が自分の送信先について実行できます

- キューで少しずつ送ります（10 秒ごとに 50 通）。送信先の同時接続の枠が埋まっているあいだは待ちます。
- 対象は、始めた時点で失敗で終わっていた送信です。その後に失敗したものや、1 通ずつ送り直したものは含みません。
- 自動で止まっていた（`disabled`）あいだのメッセージは、送信先あての記録が無いため対象になりません。一時停止（`paused`）のあいだの送信は保留になっており、再開したときに送ります。
- 途中で送信先を止める・削除すると、そこで終わります（`stopped`）。
- 使用量には数えません。

## 失敗の通知の条件

失敗の通知（Slack・Teams・Chatwork・メール・Webhook）をいつ送るかを組織ごとに選べます。管理画面の「失敗の通知」の「知らせる条件」か、MCP の `update_alert_conditions` で設定します。条件はすべての通知先で共通です。

| 条件 | 選べる値 | 既定 |
| --- | --- | --- |
| 送信の失敗 | 最初の失敗の直後、失敗が 5 分・15 分・60 分続いたとき | 60 分 |
| 自動停止 | 失敗が 5 日続いて送信先を止めたとき | — |
| 再送待ちの件数 | 環境の再送待ちが 100 通・1,000 通・10,000 通を超えたとき（日に 1 回） | 1,000 通 |
| 使用量 | 今月の送信が上限の 50%・80%・100% に届いたとき | 80% |

- 送信の失敗は、どの設定でも再送を使い切ったときに知らせます。同じ送信先の通知は 1 時間に 1 回までです。
- 同じ組織への送信の失敗の通知は、1 時間に 1 通まですぐ送ります。その 1 時間に失敗し始めたほかの送信先は、1 時間ごとに 1 通にまとめて送ります。
- 再送待ちの件数は送信先ごとではなく、環境の合計で数えます。
- 組織の owner には、この条件とは別に、使用量が上限の 80%・100% に届いたときにメールで知らせます。

## 型

```
MessageRow    = { id, event_type, consumer: { id, external_id, name }, created_at,
                  status: 'success' | 'retrying' | 'failed' | 'pending', attempts, endpoint_url }
MessageDetail = { id, event_type, consumer, created_at, idempotency_key, payload, deliveries: Delivery[] }
Delivery      = { id, endpoint_id, url, status: 'pending' | 'success' | 'retrying' | 'failed',
                  attempt_count, next_attempt_at, attempts: AttemptRow[] }
AttemptRow    = { id, at, response_status, response_head, duration_ms, error,
                  via: 'direct' | 'relay', source_ip }
Consumer      = { id, external_id, name, created_at, endpoint_count }
Endpoint      = { id, consumer_id, url, description, event_types: string[] | null, fixed_ip,
                  status: 'active' | 'paused' | 'disabled', disabled_reason, failing_since, created_at,
                  retry: RetryPolicy, compat_signature: CompatSignature | null }
RetryPolicy   = { count: 0 | 1 | 2 | 3 | 5 | 7 | 12,
                  interval: 'progressive' | '1m' | '5m' | '30m' | '1h' | '6h' }
CompatSignature = { header, content: 'body' | 'timestamp_body', encoding: 'hex' | 'base64', prefix }
Recovery      = { id, endpoint_id, since, status: 'running' | 'done' | 'stopped',
                  total, queued, created_at, finished_at }
EventType     = { name, description, archived_at }
```
