> 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/en/api-integration/webhook.md).

# Webhook

Conversation platform Webhooks: Push visitor messages and AI/human agent replies to your system in real time, with support for the MaiAgent standard format, Genesys Cloud, and custom Jinja2 templates

Webhooks are configured on a **conversation platform (Inbox)**. Whenever a new message is created in a conversation on that platform, MaiAgent sends the message to your specified endpoint through an HTTP request. You can configure multiple Webhooks for a conversation platform, each with its own message direction and format.

Go to <mark style="color:blue;">Settings</mark> → <mark style="color:blue;">Conversation Platforms</mark> → select a platform → the <mark style="color:blue;">Webhooks</mark> tab → <mark style="color:blue;">Add Webhook</mark>.

{% hint style="info" %}
When you send a message through the [Create Conversations and Messages](/tech/en/api-integration/dui-hua-yu-xun-xi.md) API, the response only indicates that the message has been created. **The AI assistant generates its reply asynchronously and sends it to you through an Outgoing Webhook**. This is the standard way to receive replies through an API integration.
{% endhint %}

## How it works <a href="#how-it-works" id="how-it-works"></a>

```mermaid
sequenceDiagram
    participant C as Your system
    participant M as MaiAgent
    participant AI as AI assistant

    C->>M: POST /api/v1/messages/ (visitor message)
    M-->>C: 201 Created (no reply included)
    M-)C: Incoming Webhook (visitor message)
    M->>AI: Generate a reply
    AI-->>M: Reply message created
    M-)C: Outgoing Webhook (AI reply)
```

When a human agent replies in the admin console, that reply is also an Outgoing message and is sent to the Outgoing Webhook. Use `sender.type` in the payload to distinguish between an AI and a human agent.

## Webhook types <a href="#directions" id="directions"></a>

| Type         | Trigger                                                                                    | Typical use case                                                                                                     |
| ------------ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
| **Outgoing** | When an AI assistant or human agent reply is created                                       | Receive AI replies and forward replies to your own frontend or messaging service                                     |
| **Incoming** | When a visitor message is created (including all channels such as API, Web Chat, and LINE) | Sync conversation records to a customer service system, data warehouse, or audit system                              |
| **Receive**  | When an external platform calls MaiAgent                                                   | Receive messages returned by an external customer service platform (currently supports Genesys Cloud Open Messaging) |

This page describes Outgoing and Incoming Webhooks.

## Configuration fields <a href="#fields" id="fields"></a>

| Field                                           | Description                                                                                                         |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| <mark style="color:blue;">Name</mark>           | Custom identifier                                                                                                   |
| <mark style="color:blue;">Type</mark>           | Outgoing/Incoming/Receive                                                                                           |
| <mark style="color:blue;">Endpoint URL</mark>   | URL that receives the push request                                                                                  |
| <mark style="color:blue;">HTTP Method</mark>    | `POST` (default)/`PUT`/`PATCH`                                                                                      |
| <mark style="color:blue;">Headers</mark>        | Custom headers added to each request (such as your system's API key)                                                |
| <mark style="color:blue;">Payload Format</mark> | **MaiAgent standard format**/**Genesys Cloud**/**Custom template** (Jinja2)                                         |
| <mark style="color:blue;">Secret Token</mark>   | When configured, each request includes an `X-Webhook-Secret: <token>` header so your endpoint can verify the source |
| <mark style="color:blue;">Enabled</mark>        | No requests are sent when disabled, but the configuration is retained                                               |

## MaiAgent standard format <a href="#standard-payload" id="standard-payload"></a>

The `Content-Type` is `application/json`. The payload is shown below (Outgoing and Incoming use the same structure, with `type` indicating the direction):

```json
{
  "id": "2491074a-6d23-4289-af45-caa12531420e",
  "type": "outgoing",
  "content": "Hello, how can I help you?",
  "created_at": "2026-09-09T10:15:30.123456+00:00",
  "conversation": {
    "id": "8e44604a-8278-4c96-96b5-3e5a0548d738",
    "title": "Question about the return process",
    "type": "individual",
    "status": "open",
    "progress_status": "ai_processing",
    "auto_reply_enabled": true,
    "assignee": null
  },
  "inbox": {
    "id": "1f9c1c1e-4d1a-4b1e-9d0e-7b2f4a5c6d7e",
    "name": "Website Customer Service",
    "channel_type": "web"
  },
  "contact": {
    "id": "c0ffee00-1111-2222-3333-444455556666",
    "name": "John Doe",
    "email": null,
    "phone_number": null
  },
  "sender": {
    "id": "5c1d3e2a-0000-4b1e-9d0e-7b2f4a5c6d7e",
    "name": "AI Customer Service Agent",
    "type": "chatbot"
  },
  "attachments": [
    {
      "id": "a1b2c3d4-0000-4b1e-9d0e-7b2f4a5c6d7e",
      "type": "image",
      "filename": "photo.jpg",
      "source_url": "https://example.com/photo.jpg"
    }
  ],
  "metadata": {}
}
```

### Field descriptions <a href="#standard-fields" id="standard-fields"></a>

| Field                             | Type           | Description                                                                                                         |
| --------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------- |
| `id`                              | string         | Message ID (UUID)                                                                                                   |
| `type`                            | string         | `incoming` (visitor message)/`outgoing` (AI or human agent reply)                                                   |
| `content`                         | string         | Message text                                                                                                        |
| `created_at`                      | string         | ISO 8601 timestamp                                                                                                  |
| `conversation.id`                 | string         | Conversation ID                                                                                                     |
| `conversation.title`              | string         | Conversation title                                                                                                  |
| `conversation.type`               | string         | `individual`/`group`                                                                                                |
| `conversation.status`             | string         | `open`/`queued` (waiting in the queue for a human agent)/`resolved`                                                 |
| `conversation.progress_status`    | string         | Current handler: `ai_processing`/`waiting_for_human`/`human_serving`/`resolved`/`open`                              |
| `conversation.auto_reply_enabled` | boolean        | `false` means that the AI has stopped replying automatically (being transferred to a human agent)                   |
| `conversation.assignee`           | object \| null | Assigned customer service agent `{ id, name }`; `null` if unassigned                                                |
| `inbox.id` / `inbox.name`         | string         | Conversation platform ID and name                                                                                   |
| `inbox.channel_type`              | string         | `web`/`line`/`messenger`/`instagram`/`telegram`/`teams`/`email`/`whatsapp`/`slack`/`team_plus`/`line_works`/`viber` |
| `contact`                         | object \| null | Visitor (contact) `{ id, name, email, phone_number }`                                                               |
| `sender.type`                     | string         | `chatbot` (AI assistant)/`user` (human agent)/`contact` (visitor)                                                   |
| `attachments[]`                   | array          | Attachments `{ id, type, filename, source_url }`; `type` is `image`/`video`/`audio`/`sticker`/`other`               |
| `metadata`                        | object         | Additional message data                                                                                             |

{% hint style="warning" %}
`attachments[].source_url` is the attachment's **source URL** (the original link provided by the external platform). This field may be empty for files stored by MaiAgent (such as files uploaded through the API or generated by AI). If you need a downloadable URL, use a custom template and retrieve `attachment.file.url` (see the example below), or call the Messages API with the message ID to retrieve the attachment.
{% endhint %}

## Custom templates (Jinja2) <a href="#custom-template" id="custom-template"></a>

If your system requires a specific JSON structure, select <mark style="color:blue;">Custom Template</mark> and write the payload using Jinja2 syntax. The rendered template must be valid JSON. It is validated once with test data when you save it.

### Available variables <a href="#template-variables" id="template-variables"></a>

| Variable       | Contents                                                                                                                                                |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message`      | `id`, `type`, `content`, `created_at` (datetime), `metadata`                                                                                            |
| `conversation` | `id`, `title`, `type`, `status`, `progress_status`, `auto_reply_enabled`, `assignee` (may be `None`; when present: `assignee.id`, `assignee.user.name`) |
| `inbox`        | `id`, `name`, `channel_type`                                                                                                                            |
| `contact`      | `id`, `name`, `email`, `phone_number`, `source_id` (the `sourceId` supplied through the identity sync API); may be `None`                               |
| `sender`       | `id`, `name`; `sender_type` is `chatbot`/`user`/`contact`                                                                                               |
| `attachments`  | List of attachments, each containing `id`, `type`, `filename`, `source_url`, and `file` (`file.url` is a downloadable URL)                              |
| `now`          | Current rendering time (datetime)                                                                                                                       |

Always pass strings through the `| tojson` filter so that quotation marks and line breaks are escaped correctly.

### Example: Return your system's user ID and a downloadable attachment 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() }}"
}
```

## Delivery behavior <a href="#delivery" id="delivery"></a>

* Each message is sent once to each enabled Webhook. Requests time out after **30 seconds**.
* A **2xx** response is considered successful. Non-2xx responses and timeouts are recorded as failures and are **not retried automatically**. If your system requires reliable delivery, persist the request before returning a 2xx response and deduplicate requests by message `id`.
* The endpoint URL is protected against SSRF. Destinations on private networks and internal addresses are rejected.
* You can view each push request's content, response, and status code on the <mark style="color:blue;">Webhook</mark> tab of the AI assistant page for troubleshooting.

## Manage Webhooks through the API <a href="#manage-by-api" id="manage-by-api"></a>

You can also use the API to create and maintain Webhooks (the fields correspond to the admin console settings). See [Quickstart](/tech/en/api-integration/quickstart.md) for authentication instructions.

```bash
# List / Create
GET  https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/
POST https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/

# Update / Delete
PATCH  https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/{webhookId}/
DELETE https://api.maiagent.ai/api/v1/inboxes/{inboxId}/webhooks/{webhookId}/
```

```json
{
  "name": "Sync replies to the customer service system",
  "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"
}
```

You can retrieve `inboxId` from `inbox.id` in the [Create Conversation](/tech/en/api-integration/dui-hua-yu-xun-xi.md) response, or view it on the conversation platform settings page in the admin console.

## Difference from the human handoff Webhook <a href="#vs-handoff-webhook" id="vs-handoff-webhook"></a>

The Webhook described on this page sends data for each **message**. If your system needs to know **when a conversation is handed off to a human agent and when it returns to AI** (for example, when your customer service system takes over), use the separate human handoff Webhook based on the conversation lifecycle. See [Third-party Customer Service System Integration (Including Human Handoff)](/tech/en/api-integration/customer-service-system-integration.md).

{% hint style="warning" %}
**Release status: Pending official release.** The link above describes the new four-event human handoff contract and its nested `id`/`occurred_at` payload, which has not yet been released to the production environment. Do not enable a production integration based on the new contract described there. First confirm with the MaiAgent team that your environment has been updated. This release status applies only to the new human handoff contract described at the link; the message Webhook described on this page is available for normal use.
{% endhint %}


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.maiagent.ai/tech/en/api-integration/webhook.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
