> ## Documentation Index
> Fetch the complete documentation index at: https://docs.waffo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook の追加

> ストアに新しい Webhook エンドポイントを設定する

ストアに Webhook エンドポイントを追加します。各ストアでは、すべてのチャネルにわたって最大 **20 個の Webhook** を持つことができます。

```
POST /v1/actions/store/add-webhook
```

**認証：** API Key（owner または admin ロールが必要）

## リクエストボディ

| フィールド      | 型              | 必須  | 説明                                                                                          |
| ---------- | -------------- | --- | ------------------------------------------------------------------------------------------- |
| `storeId`  | string         | Yes | Store ID（Short ID フォーマット `STO_xxx`）                                                         |
| `channel`  | string         | Yes | `http`、`feishu`、`discord`、`telegram`、`slack` のいずれか                                          |
| `url`      | string         | Yes | Webhook 送信先 URL（HTTPS。マーチャントは URL が選択したチャネルと一致することを保証）                                      |
| `events`   | string\[]      | Yes | 購読するイベントタイプ — 例：`["order.completed", "refund.succeeded"]`。空配列の場合、この Webhook にはイベントが配信されません。 |
| `testMode` | boolean        | Yes | `true` = テストトランザクションで発火、`false` = 本番で発火                                                     |
| `secret`   | string \| null | No  | チャネル固有の認証情報（例：Telegram の `chat_id`）。不透明なテキストとして保存されます。                                      |

## リクエスト例

<CodeGroup>
  ```typescript SDK theme={"system"}
  import { WaffoPancake } from "@waffo/pancake-ts";

  const client = new WaffoPancake({
    merchantId: process.env.WAFFO_MERCHANT_ID!,
    privateKey: process.env.WAFFO_PRIVATE_KEY!,
  });

  // Standard HTTPS webhook (RSA-signed envelope)
  const { webhook } = await client.webhooks.add({
    storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
    channel: "http",
    url: "https://example.com/webhooks/pancake",
    events: ["order.completed", "refund.succeeded"],
    testMode: false,
  });
  ```

  ```typescript SDK (Discord) theme={"system"}
  // Discord webhook — uses Discord's native embed format
  await client.webhooks.add({
    storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
    channel: "discord",
    url: "https://discord.com/api/webhooks/123456789/abc-def-ghi",
    events: ["order.completed"],
    testMode: false,
  });
  ```

  ```typescript SDK (Telegram) theme={"system"}
  // Telegram bot — chat_id goes in secret
  await client.webhooks.add({
    storeId: "STO_2aUyqjCzEIiEcYMKj7TZtw",
    channel: "telegram",
    url: "https://api.telegram.org/bot123456:ABC-DEF/sendMessage",
    events: ["order.completed", "refund.failed"],
    testMode: false,
    secret: "8737101383",
  });
  ```

  ```bash cURL theme={"system"}
  curl -X POST https://api.waffo.com/v1/actions/store/add-webhook \
    -H "Content-Type: application/json" \
    -H "X-Merchant-Id: $WAFFO_MERCHANT_ID" \
    -H "X-Signature: ..." \
    -d '{
      "storeId": "STO_2aUyqjCzEIiEcYMKj7TZtw",
      "channel": "http",
      "url": "https://example.com/webhooks/pancake",
      "events": ["order.completed"],
      "testMode": false
    }'
  ```
</CodeGroup>

## 成功レスポンス (200)

```json theme={"system"}
{
  "data": {
    "webhook": {
      "id": "11111111-2222-3333-4444-555555555555",
      "storeId": "uuid-of-store",
      "channel": "http",
      "url": "https://example.com/webhooks/pancake",
      "events": ["order.completed", "refund.succeeded"],
      "testMode": false,
      "secret": null,
      "createdAt": "2026-05-07T00:00:00.000Z",
      "updatedAt": "2026-05-07T00:00:00.000Z"
    }
  }
}
```

<Note>
  返される `webhook.id` は UUID であり、Short ID ではありません — Webhook ID は `IdPrefix` のスコープに含まれません。`update-webhook` および `remove-webhook` にはそのまま渡してください。
</Note>

## レスポンスフィールド

| フィールド       | 型              | 説明                                                             |
| ----------- | -------------- | -------------------------------------------------------------- |
| `id`        | string         | Webhook UUID                                                   |
| `storeId`   | string         | 所属ストアの UUID                                                    |
| `channel`   | string         | Webhook チャネル（`http`、`feishu`、`discord`、`telegram`、または `slack`） |
| `url`       | string         | 送信先 Webhook URL                                                |
| `events`    | string\[]      | 購読するイベントタイプ                                                    |
| `testMode`  | boolean        | `true` はテストトランザクションで発火、`false` は本番で発火                          |
| `secret`    | string \| null | チャネル固有の認証情報（不透明テキスト）、未設定の場合は `null`                            |
| `createdAt` | string         | 作成タイムスタンプ（ISO 8601）                                            |
| `updatedAt` | string         | 最終更新タイムスタンプ（ISO 8601）                                          |

## エラー

> **リトライポリシー**：4xx は一切リトライしない — リクエストを修正してから再送信。5xx は指数バックオフでリトライ（5s 開始、最大 3 回）。

| ステータス | `errors[0].message`                                                      | 意味                                   | 推奨処理                       |
| ----- | ------------------------------------------------------------------------ | ------------------------------------ | -------------------------- |
| 400   | `Missing required field: storeId`                                        | `storeId` が指定されていない                  | リクエストボディを修正して再送信           |
| 400   | `Expected format: STO_xxx, got "..."`                                    | `storeId` Short ID のデコード失敗           | `storeId` のフォーマットを修正して再送信  |
| 400   | `testMode must be a boolean`                                             | `testMode` が boolean ではない            | boolean 値を渡して再送信           |
| 400   | `Invalid channel: must be one of http, feishu, discord, telegram, slack` | `channel` が許可リストにない                  | 許可された channel を使用          |
| 400   | `Invalid URL format`                                                     | `url` が有効な URL ではない                  | URL を修正して再送信               |
| 400   | `events must be a string array`                                          | `events` が文字列配列ではない                  | 文字列配列を渡して再送信               |
| 400   | `secret must be a string or null`                                        | `secret` が string または null ではない      | string または null を渡して再送信    |
| 400   | `Webhook limit reached (max 20 per store)`                               | ストアの Webhook 数が 20 に達している            | 既存の Webhook を先に削除          |
| 401   | `Missing merchantId in request context`                                  | API Key 認証で merchant が解決されなかった       | API Key ヘッダーと署名を確認         |
| 403   | `Not authorized to manage webhooks for this store`                       | マーチャントがストアの `owner` または `admin` ではない | マーチャントのストアでの役割を確認          |
| 500   | `Internal server error`                                                  | サーバ側の予期しない障害                         | 指数バックオフでリトライ（5s 開始、最大 3 回） |
