> For the complete documentation index, see [llms.txt](https://docs.maiagent.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.maiagent.ai/tech/ja/serve/webhook.md).

# Webhook

会話プラットフォームの Webhook：訪問者のメッセージと AI／有人対応の返信をリアルタイムで自社システムに送信します。MaiAgent 標準フォーマット、Genesys Cloud、カスタム Jinja2 テンプレートに対応しています

Webhook は\*\*会話プラットフォーム（Inbox）\*\*に設定します。そのプラットフォームの会話で新しいメッセージが作成されるたびに、MaiAgent はそのメッセージを HTTP リクエストで指定したエンドポイントに送信します。1 つの会話プラットフォームに複数の Webhook を設定でき、それぞれ送信するメッセージの方向とフォーマットを決められます。

設定場所：<mark style="color:blue;">カスタマーサービス会話</mark> → <mark style="color:blue;">会話プラットフォーム</mark> → プラットフォームを編集 → <mark style="color:blue;">Webhook 設定</mark> → <mark style="color:blue;">Webhook を追加</mark>。API プラットフォームの <mark style="color:blue;">Webhook 設定</mark>は、左側の「連携とログイン」グループにあります。LINE などのメッセージングプラットフォームでは上部のタブバーにあります。Web および社内向けプラットフォームにはこのタブはありません。管理画面での操作は、ユーザーマニュアルの [Webhook](https://docs.maiagent.ai/build/conversation-settings/webhook) を参照してください。

{% hint style="info" %}
[会話とメッセージの作成](/tech/ja/api-integration/conversations-and-messages.md) API でメッセージを送信した場合、レスポンスはメッセージが作成されたことだけを示します。**Agent の返信は非同期に生成され、Outgoing Webhook で送信されます**。これが API 連携で返信を受け取る標準的な方法です。
{% endhint %}

## 仕組み <a href="#how-it-works" id="how-it-works"></a>

```mermaid
sequenceDiagram
    participant C as 自社システム
    participant M as MaiAgent
    participant AI as Agent

    C->>M: POST /api/v1/messages/（訪問者メッセージ）
    M-->>C: 201 作成済み（返信は含まない）
    M-)C: Incoming Webhook（訪問者メッセージ）
    M->>AI: 返信を生成
    AI-->>M: 返信メッセージを作成
    M-)C: Outgoing Webhook（AI の返信）
```

有人対応の担当者が管理画面で返信した場合も、その返信は 1 件の Outgoing メッセージとなり、Outgoing Webhook に送信されます。payload の `sender.type` で、AI と有人対応のどちらによる返信かを区別できます。

## Webhook の種類 <a href="#directions" id="directions"></a>

| 種類           | トリガー                                               | 代表的な用途                                                                    |
| ------------ | -------------------------------------------------- | ------------------------------------------------------------------------- |
| **Outgoing** | Agent または有人対応の担当者の返信が作成されたとき                       | AI の返信の取得、返信の自社フロントエンドやメッセージングアプリへの転送                                     |
| **Incoming** | 訪問者のメッセージが作成されたとき（API、Web Chat、LINE などすべてのチャネルを含む） | 会話記録のカスタマーサービスシステム、データウェアハウス、監査への同期                                       |
| **Receive**  | 外部プラットフォームから MaiAgent を呼び出す                        | 外部のカスタマーサービスプラットフォームから返送されたメッセージの受信（現在は Genesys Cloud Open Messaging に対応） |

このページでは Outgoing と Incoming について説明します。Genesys Cloud の Receive と Genesys Cloud フォーマットについては、[Genesys Cloud 連携](/tech/ja/serve/customer-service-system-integration/genesys-cloud-integration.md)を参照してください。

## 設定項目 <a href="#fields" id="fields"></a>

| 項目                                                                     | 説明                                                                                                                                                                                                                                               |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <mark style="color:blue;">名前</mark>                                    | 任意の識別名                                                                                                                                                                                                                                           |
| <mark style="color:blue;">種類</mark>                                    | Outgoing／Incoming／Receive                                                                                                                                                                                                                        |
| <mark style="color:blue;">Webhook URL</mark>                           | 送信を受け取る URL                                                                                                                                                                                                                                      |
| <mark style="color:blue;">HTTP Method</mark>                           | `POST`（デフォルト）／`PUT`／`PATCH`                                                                                                                                                                                                                      |
| <mark style="color:blue;">Headers</mark>                               | 毎回のリクエストに付加するカスタムヘッダー（例：自社システムの API キー）                                                                                                                                                                                                          |
| <mark style="color:blue;">Payload フォーマット</mark>                        | **MaiAgent 標準フォーマット**／**Genesys Cloud**／**カスタムテンプレート**（Jinja2）                                                                                                                                                                                   |
| <mark style="color:blue;">有効状態</mark>                                  | 無効にすると送信されませんが、設定は保持されます                                                                                                                                                                                                                         |
| <mark style="color:blue;">Secret Token</mark>（API フィールド `secretToken`） | 署名用のシークレットです。設定すると、送信のたびに HMAC-SHA256 署名とタイムスタンプのヘッダーが付与されます。[署名とタイムスタンプの検証](#webhook-signature)を参照してください。書き込み専用で、照会時には返されません。管理画面で編集する際に空欄にすると、元の値が維持されます。<mark style="color:blue;">Payload フォーマット</mark>が Genesys Cloud の場合、管理画面にはこの項目は表示されません |

{% hint style="info" %} <mark style="color:blue;">Headers</mark> に `X-MaiAgent-Signature-256`、`X-MaiAgent-Timestamp`、`X-MaiAgent-Delivery-Attempt` と同名（大文字小文字は区別しません）のカスタムヘッダーがある場合、送信前に削除され、常に MaiAgent が生成した値が使用されます。
{% endhint %}

## MaiAgent 標準フォーマット <a href="#standard-payload" id="standard-payload"></a>

`Content-Type: application/json` で、内容は次のとおりです（Outgoing と Incoming は同じ構造で、`type` で方向を区別します）。

```json
{
  "id": "2491074a-6d23-4289-af45-caa12531420e",
  "type": "outgoing",
  "content": "こんにちは。どのようなご用件でしょうか？",
  "created_at": "2026-09-09T10:15:30.123456+00:00",
  "conversation": {
    "id": "8e44604a-8278-4c96-96b5-3e5a0548d738",
    "title": "返品手続きの問い合わせ",
    "type": "individual",
    "status": "open",
    "progress_status": "ai_processing",
    "auto_reply_enabled": true,
    "assignee": null
  },
  "inbox": {
    "id": "1f9c1c1e-4d1a-4b1e-9d0e-7b2f4a5c6d7e",
    "name": "公式サイトサポート",
    "channel_type": "web"
  },
  "contact": {
    "id": "c0ffee00-1111-2222-3333-444455556666",
    "name": "山田 太郎",
    "email": null,
    "phone_number": null
  },
  "sender": {
    "id": "5c1d3e2a-0000-4b1e-9d0e-7b2f4a5c6d7e",
    "name": "AI カスタマーサポート",
    "type": "chatbot"
  },
  "attachments": [
    {
      "id": "a1b2c3d4-0000-4b1e-9d0e-7b2f4a5c6d7e",
      "type": "image",
      "filename": "photo.jpg",
      "source_url": "https://example.com/photo.jpg"
    }
  ],
  "metadata": {}
}
```

### フィールドの説明 <a href="#standard-fields" id="standard-fields"></a>

| フィールド                             | 型              | 説明                                                                                                                  |
| --------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------- |
| `id`                              | string         | メッセージ ID（UUID）                                                                                                      |
| `type`                            | string         | `incoming`（訪問者のメッセージ）／`outgoing`（AI または有人対応の返信）                                                                     |
| `content`                         | string         | メッセージのテキスト                                                                                                          |
| `created_at`                      | string         | ISO 8601 形式の時刻                                                                                                      |
| `conversation.id`                 | string         | 会話 ID                                                                                                               |
| `conversation.title`              | string         | 会話のタイトル                                                                                                             |
| `conversation.type`               | string         | `individual`／`group`                                                                                                |
| `conversation.status`             | string         | `open`／`queued`（有人対応の待機中）／`resolved`                                                                                |
| `conversation.progress_status`    | string         | 現在の対応者：`ai_processing`／`waiting_for_human`／`human_serving`／`resolved`／`open`                                        |
| `conversation.auto_reply_enabled` | boolean        | `false` は AI が自動返信を停止していること（有人対応に引き継ぎ中）を示します                                                                        |
| `conversation.assignee`           | object \| null | 引き継いだサポートメンバー `{ id, name }`。未割り当ての場合は `null`                                                                       |
| `inbox.id` / `inbox.name`         | string         | 会話プラットフォームの ID と名前                                                                                                  |
| `inbox.channel_type`              | string         | `web`／`line`／`messenger`／`instagram`／`telegram`／`teams`／`email`／`whatsapp`／`slack`／`team_plus`／`line_works`／`viber` |
| `contact`                         | object \| null | 訪問者（連絡先）`{ id, name, email, phone_number }`                                                                         |
| `sender.type`                     | string         | `chatbot`（Agent）／`user`（有人対応の担当者）／`contact`（訪問者）                                                                    |
| `attachments[]`                   | array          | 添付ファイル `{ id, type, filename, source_url }`。`type` は `image`／`video`／`audio`／`sticker`／`other`                      |
| `metadata`                        | object         | メッセージの付加データ                                                                                                         |

{% hint style="warning" %}
`attachments[].source_url` は添付ファイルの**ソース URL**（外部プラットフォームから渡された元のリンク）です。MaiAgent が保存したファイル（例：API でアップロードされたファイルや AI が生成したファイル）では、このフィールドが空になる場合があります。ダウンロード可能な URL が必要な場合は、カスタムテンプレートを使用して `attachment.file.url` を取得する（下記のサンプルを参照）か、メッセージ ID を指定してメッセージ API を呼び出し、添付ファイルを取得してください。
{% endhint %}

## カスタムテンプレート（Jinja2） <a href="#custom-template" id="custom-template"></a>

システムが特定の JSON 構造を要求する場合は、<mark style="color:blue;">カスタムテンプレート</mark>を選択し、Jinja2 構文で payload を記述します。テンプレートのレンダリング結果は有効な JSON である必要があり、保存時にテストデータで一度検証されます。

### 使用できる変数 <a href="#template-variables" id="template-variables"></a>

| 変数             | 内容                                                                                                                                         |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `message`      | `id`、`type`、`content`、`created_at`（datetime）、`metadata`                                                                                    |
| `conversation` | `id`、`title`、`type`、`status`、`progress_status`、`auto_reply_enabled`、`assignee`（`None` の場合があります。値がある場合は `assignee.id`、`assignee.user.name`） |
| `inbox`        | `id`、`name`、`channel_type`                                                                                                                 |
| `contact`      | `id`、`name`、`email`、`phone_number`、`source_id`（ID 同期 API で指定した `sourceId`）。`None` の場合があります                                                 |
| `sender`       | `id`、`name`。`sender_type` は `chatbot`／`user`／`contact`                                                                                     |
| `attachments`  | 添付ファイルのリスト。各要素に `id`、`type`、`filename`、`source_url`、`file`（`file.url` がダウンロード可能な URL）があります                                                 |
| `now`          | レンダリング時の時刻（datetime）                                                                                                                       |

文字列は必ず `| tojson` フィルターを通して出力してください。これにより、引用符や改行が正しくエスケープされます。

### サンプル：自社システムのユーザー ID とダウンロード可能な添付ファイル URL を返す <a href="#template-example" id="template-example"></a>

```jinja
{
  "external_user_id": {{ (contact.source_id if contact else "") | tojson }},
  "conversation_id": "{{ conversation.id }}",
  "direction": "{{ message.type }}",
  "from_human_agent": {{ (sender_type == "user") | tojson }},
  "text": {{ message.content | tojson }},
  "attachments": [{% for a in attachments %}{
    "type": "{{ a.type }}",
    "url": {{ (a.file.url if a.file else a.source_url) | tojson }}
  }{% if not loop.last %},{% endif %}{% endfor %}],
  "sent_at": "{{ message.created_at.isoformat() }}"
}
```

## 署名とタイムスタンプの検証 <a href="#webhook-signature" id="webhook-signature"></a>

Webhook に Secret Token を設定している場合、送信のたびに次のヘッダーが付与されます。受信側はこれを使って、リクエストが MaiAgent から送信されたものであること、内容が改ざんされていないことを確認し、期限切れのリプレイリクエストを拒否できます。

| ヘッダー                          | 内容                                                                                                   |
| ----------------------------- | ---------------------------------------------------------------------------------------------------- |
| `X-MaiAgent-Timestamp`        | 今回の送信時点の Unix 時間（秒）                                                                                  |
| `X-MaiAgent-Signature-256`    | `sha256=<hex>`：Secret Token を鍵として、「`タイムスタンプ` + `.` + 元の request body」に対して計算した HMAC-SHA256（16 進数の小文字） |
| `X-MaiAgent-Delivery-Attempt` | 何回目の試行か。初回は `1`（Secret Token の設定有無にかかわらず付与されます）                                                      |
| `X-Webhook-Secret`            | Secret Token の平文です。この値のみを照合する既存の受信側との互換性のため、当面は引き続き送信されますが、使用は推奨されません。署名の検証に切り替えてください                |

### 検証手順 <a href="#verify-steps" id="verify-steps"></a>

1. **元の request body のバイト列**を読み取ります。先に JSON を解析してから再シリアライズしないでください。MaiAgent が送信するのは、空白のないコンパクトな UTF-8 エンコードの JSON で、中国語はエスケープされません。再シリアライズ後のバイト列は通常異なるため、署名が一致しなくなります。
2. `X-MaiAgent-Timestamp` を取得し、受信側の現在時刻と比較します。設定した許容範囲（例：5 分）を超えている場合は拒否します。再送を含むすべての試行で、送信時点の時刻でタイムスタンプと署名が再計算されるため、再送がタイムスタンプの古さを理由に拒否されることはありません。
3. Secret Token で `HMAC-SHA256(secret, f"{timestamp}.".encode() + raw_body)` を計算し、16 進数に変換して、先頭に `sha256=` を付けます。
4. 定数時間比較（例：Python の `hmac.compare_digest`）で `X-MaiAgent-Signature-256` と比較し、一致しない場合は `401` を返します。

```python
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300  # タイムスタンプの許容範囲（任意）


def verify_maiagent_signature(secret: str, raw_body: bytes, timestamp: str | None, signature: str | None) -> bool:
    if not timestamp or not signature or not timestamp.isdigit():
        return False
    if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
        return False
    expected = "sha256=" + hmac.new(secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

{% hint style="warning" %}
有人対応への引き継ぎ Webhook の署名ヘッダーも `X-MaiAgent-Signature-256` ですが、request body のみに署名し、タイムスタンプは含みません。1 つの受信側で両方を処理する場合は、パスごとに分けて検証してください。[サードパーティ製カスタマーサービスシステムとの連携（有人対応への引き継ぎを含む）](/tech/ja/serve/customer-service-system-integration.md)を参照してください。
{% endhint %}

## 送信の動作 <a href="#delivery" id="delivery"></a>

* 有効な Webhook ごとに、メッセージ 1 件につき 1 回送信します。リクエストのタイムアウトは **30 秒**です。
* **2xx** のレスポンスを成功とみなします。
* エンドポイント URL には SSRF 対策が適用されます。プライベートネットワークや内部アドレスへの送信先は拒否されます。
* 再送を含む試行ごとに、リクエスト内容、レスポンス、ステータスコードが 1 件ずつ記録されます。トラブルシューティングのために、Agent ページの <mark style="color:blue;">Webhook</mark> タブで確認できます。

### 自動再送 <a href="#retry" id="retry"></a>

| 各試行の結果                                                                            | 処理                                                                         |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| タイムアウト、接続失敗、`5xx`、`429`                                                           | 一時的な失敗とみなし、約 **1 分、5 分、15 分**後に各 1 回再送します。**再送は最大 3 回**です（初回を含めて合計 4 回の試行） |
| その他の `4xx`（例：`400`、`401`、`404`）                                                   | 失敗として記録し、再送しません                                                            |
| SSRF 対策により送信先がブロックされた場合（ドメイン名の解決失敗も含み、一時的な DNS 障害も該当します）、カスタムテンプレートのレンダリングに失敗した場合 | 失敗として記録し、再送しません                                                            |

* 再送の request body は、初回と**バイト単位で完全に同一**です。`X-MaiAgent-Delivery-Attempt` は順に `2`、`3`、`4` となり、タイムスタンプと署名は送信時点で再計算されます。
* URL、カスタムヘッダー、Secret Token は、再送時点の Webhook 設定が使用されます。再送前に Webhook が無効化または削除された場合、またはメッセージが削除された場合は、再送されません。
* 3 回の再送がすべて失敗した場合は断念し、それ以降は送信されません。データを補完する必要がある場合は、`GET /api/v1/messages/?conversation={conversationId}` を呼び出して、その会話のメッセージを照会してください（`conversation` パラメータは必須です。[会話とメッセージの返信（ストリーミング／同期）](/tech/ja/api-integration/dui-hua-yu-xun-xi-hui-fu-chuan-liu-tong-bu.md)を参照してください）。
* 再送の間隔は固定で、レスポンスの `Retry-After` ヘッダーは参照しません。
* Payload フォーマットが Genesys Cloud の場合、`401` を受け取るとまず access token を更新して即座に 1 回再送します。この再送は上記の 3 回の再送枠には含まれません。この再送で一時的な失敗が発生した場合は上の表のスケジュールで再送し、それでも `401` の場合は停止します。

### 冪等性に関する推奨事項 <a href="#idempotency" id="idempotency"></a>

再送があるため、送信のセマンティクスは「少なくとも 1 回は届く」です。エンドポイントがすでに処理を完了していても、レスポンスがタイムアウトしたか `5xx` を返した場合、同じメッセージが再度届きます。次の対応を推奨します。

* payload のメッセージ `id` で重複排除し、処理済みの `id` には直接 `2xx` を返します。カスタムテンプレートを使用する場合は、テンプレートに `message.id` を残してください。残さないと重複排除できません。
* 重複排除には、`X-MaiAgent-Timestamp` や署名ではなく、メッセージ `id` を使用してください。この 2 つは試行のたびに変わります。
* 受信した内容を保存（データベースやキューに書き込み）してから `2xx` を返し、時間のかかる処理は非同期にします。これにより、30 秒のタイムアウトを超えて不要な再送が発生するのを避けられます。
* このリクエストが今後も処理できないと確定している場合にのみ `4xx` を返してください（再送されません）。一時的に処理できない場合（例：下流サービスの障害）は、`5xx` または `429` を返し、MaiAgent に後で再送させてください。

## API による Webhook の管理 <a href="#manage-by-api" id="manage-by-api"></a>

管理画面のほか、API でも Webhook を作成・管理できます（フィールドは管理画面の設定に対応しています）。認証方法は[クイックスタート](/tech/ja/api-integration/quickstart.md)を参照してください。

```bash
# 一覧取得 / 追加
GET  https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/
POST https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/

# 更新 / 削除
PATCH  https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/{webhookId}/
DELETE https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/{webhookId}/
```

```json
{
  "name": "返信をカスタマーサービスシステムに同期",
  "direction": "outgoing",
  "isEnabled": true,
  "url": "https://your-system.example.com/maiagent/reply",
  "method": "POST",
  "headers": { "X-Api-Key": "your-key" },
  "templatePreset": "maiagent_standard",
  "customTemplate": null,
  "secretToken": "shared-secret"
}
```

`inboxId` は、[会話の作成](/tech/ja/api-integration/conversations-and-messages.md)のレスポンスに含まれる `inbox.id` から取得するか、管理画面の会話プラットフォーム設定ページで確認できます。

## 「有人対応への引き継ぎ Webhook」との違い <a href="#vs-handoff-webhook" id="vs-handoff-webhook"></a>

このページの Webhook は**メッセージ**単位で送信します。会話が**いつ有人対応に引き継がれ、いつ AI に戻ったか**を把握する必要がある場合（例：自社のカスタマーサービスシステムが対応を引き継ぐ場合）は、会話のライフサイクル単位で送信する、別の有人対応への引き継ぎ Webhook を使用してください。[サードパーティ製カスタマーサービスシステムとの連携（有人対応への引き継ぎを含む）](/tech/ja/serve/customer-service-system-integration.md)を参照してください。


---

# 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://docs.maiagent.ai/tech/ja/serve/webhook.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.
