入站配置(Inbound)使用指南
什么是入站配置?
入站配置(Inbound)允许你的接口(Endpoint)接收来自第三方系统的 Webhook 数据,并自动将其转换成标准的消息格式后推送到绑定的渠道。
简单来说:用于解决无法修改发送端消息结构内容的问题
典型使用场景
| 场景 | 说明 |
|---|---|
| Grafana 告警 | 服务器异常时自动推送告警到微信 |
| Prometheus 告警 | 监控指标超阈值时发送通知 |
| GitHub 事件 | PR 合并、Issue 创建时收到提醒 |
| Emby 通知 | 影视库播放、转码完成时推送消息 |
快速开始
第一步:创建接口并绑定渠道
- 进入「接口管理」页面,点击「新建接口」
- 设置名称(如"Grafana 告警"),系统会自动生成访问令牌
- 在渠道列表中勾选要接收消息的渠道(如企业微信、Telegram 等)
- 保存
第二步:开启入站配置
- 在接口卡片中点击「入站配置」
- 打开「启用入站接收」开关
- 选择数据来源类型
- 根据所选类型填写字段映射规则
- 点击「保存配置」
第三步:配置外部系统
将页面底部显示的接收地址填入第三方系统的 Webhook 配置中。令牌有两种传递方式:
方式一:令牌放在 URL 路径中(默认)
POST https://你的域名/api/inbound/你的令牌方式二:令牌放在 Authorization 请求头中(更安全,推荐)
当第三方系统不方便把令牌暴露在 URL 中(URL 可能被日志记录、代理缓存)时,可以使用请求头传递令牌:
POST https://你的域名/api/inbound
Authorization: Bearer 你的令牌两种方式效果完全一致。使用方式二时,请求体(payload)的填写规则与方式一完全相同。 如果令牌不以
Bearer前缀开头,也可以直接写成Authorization: 你的令牌。
完成!现在当第三方系统触发 Webhook 时,消息就会自动推送到你绑定的渠道了。
界面说明
开启入站后,你会看到以下配置区域:
数据来源类型
选择一个预设模板或选择「通用」进行完全自定义。每种模板对应一类常见的数据格式。
| 模板 | 适用场景 |
|---|---|
| Grafana | Grafana 告警通知 |
| Prometheus | Prometheus AlertManager 告警 |
| GitHub | GitHub Webhook 事件(PR、Issue 等) |
| Emby | Emby/Jellyfin 媒体库事件通知 |
| 通用 | 其他任意格式的数据(需手动填写字段映射) |
注意:选择「Grafana」「Prometheus」「GitHub」「Emby」等预设模板时,字段映射由系统自动处理,无需手动填写。只有选择「通用」时才会显示字段映射编辑区域。
字段映射规则(仅「通用」模式可见)
选择「通用」模板后,会出现四个输入项:
标题字段(多行文本框)
每行填写一条规则。最终结果由所有行的输出按顺序拼接而成。
内容字段(多行文本框)
与标题字段用法相同。
消息类型(下拉选择)
| 选项 | 说明 |
|---|---|
| 纯文本 (text) | 默认选项,适合大多数推送渠道 |
| Markdown | 支持 Markdown 格式的渠道可使用此选项 |
| HTML | 支持 HTML 格式的渠道可使用此选项 |
附加数据 (extraData)(单份 JSON 模板)
用于承载各个渠道特有的、标准 title / content 之外的参数(如图文消息、卡片消息、图片消息等)。它不是一个 JSONPath,而是一份 JSON 模板:
- 静态部分会原样保留;
- 需要动态取值的地方用
$.xxx.yyy占位,运行时从 Webhook 的 payload 中提取并替换; - 解析后的整份 JSON 会原样透传给渠道端(按渠道类型读取对应命名空间,如
wecom、telegram); - 必须填写合法 JSON,否则无法保存(输入框下方会实时提示错误);
- 留空表示不使用附加数据。
注意:与「标题字段 / 内容字段」不同,
extraData只能填写一个 JSON 模板,不支持多行多路径。
占位符解析规则
| 模板中的写法 | 系统如何处理 |
|---|---|
"$.msg.title" | 从 payload 的该路径取值并替换;取不到时替换为 null(保留该键) |
"系统自动截图" | 不以 $. 开头,作为固定文字原样保留 |
{"a": 1} | 对象 / 数组会递归处理,对其中的字符串值同样应用上述规则 |
填写规则
这是最核心的部分。文本框中的每一行会被独立处理,理解以下规则就能灵活配置任意格式。
规则总览
| 你填写的行内容 | 系统如何处理 | 说明 |
|---|---|---|
$.alerts[0].labels.alertname | 从原始数据的该路径提取值 | 以 $. 开头 = JSONPath 提取 |
---告警详情--- | 直接当作固定文字原样保留 | 不以 $. 开头 = 字面量 |
\n | 产生一个换行 | 转义字符,用于在行内插入换行 |
\t | 产生一个制表符(缩进) | 转义字符,用于对齐排版 |
| (空行) | 自动忽略 | 空内容不参与拼接 |
核心行为
每行独立处理,按书写顺序拼接成最终结果。
- 以
$.开头的行 → 尝试从原始数据中取值,取到了就拼接,取不到就跳过该行 - 不以
$.开头的行 → 作为固定文字直接拼接(支持\n\t转义) - 最终结果 = 所有有效行的输出无缝连接
重要限制
不要在同一行内混写文字和路径。例如下面这种写法是错误的:
❌ 来自Grafana的告警:$.alerts[0].labels.alertname这整行会被当作普通文字原样输出(因为不以 $. 开头),其中的 $.alerts... 不会被解析为路径。
正确做法是把它们分到不同的行:
✅ 来自Grafana的告警:
$.alerts[0].labels.alertname使用换行
由于每行的输出会无缝连接,如果你希望在不同信息之间换行显示,有两种方式:
方式一:用 \n 单独一行插入换行
主机: $.hostname
\n
指标: $.metric = $.value$.unit
\n
状态: $.status结果:
主机: web-server-03
指标: disk_usage = 87%
状态: warning\n 是转义字符,会被解析为真正的换行符。同样地,\t 会被解析为制表符。
方式二:在固定文本行末尾写 \n
--- 告警详情 ---\n
$.alerts[0].annotations.message
\n--- 请及时处理 ---JSONPath 语法基础
JSONPath 路径用于从原始数据中定位并提取值。
基本写法
| 填写的内容 | 含义 |
|---|---|
$.title | 取顶层的 title 字段 |
$.name | 取顶层的 name 字段 |
$.alerts[0] | 取 alerts 数组的第 1 个元素 |
$.alerts[0].labels.alertname | 取第 1 个告警的 alertname 标签 |
$[0].Title | 取数组的第 1 个元素的 Title 属性 |
$.sender.login | 取 sender 对象下的 login 字段 |
数组索引从 0 开始
$.alerts[0] ← 第 1 条告警
$.alerts[1] ← 第 2 条告警
$.alerts[2] ← 第 3 条告警支持多层嵌套,没有深度限制。
配置示例
示例一:最简单的单字段映射
假设外部系统发送的数据如下:
{
"title": "服务器异常",
"message": "CPU 使用率达到 95%"
}在界面中的填写方式:
标题字段(填一行):
$.title内容字段(填一行):
$.message消息类型:纯文本 (text)
最终推送结果:
- 标题:
服务器异常 - 正文:
CPU 使用率达到 95%
示例二:多路径提取
假设你希望从多个可能的字段中提取内容,只要有值的都会被拼接到一起:
{
"alert_name": "磁盘满",
"summary": "磁盘使用率超过 98%",
"body": "请立即清理"
}内容字段(填多行,每行一个路径):
$.summary
$.body
$.message处理过程:
$.summary→ 找到值"磁盘使用率超过 98%"→ 保留$.body→ 找到值"请立即清理"→ 保留$.message→ 数据中没有这个字段 → 跳过
最终结果:磁盘使用率超过 98%请立即清理
每条路径独立判断,能取到值的就拼上,取不到的不影响其他行。不是"取第一个就停止",而是"全部都尝试,有效的全拼接"。
示例三:添加前缀(文字与路径分行)
你希望在动态值前面加上固定的前缀文字:
{
"alerts": [
{
"labels": { "alertname": "HighCPU" }
}
]
}标题字段(分两行填写):
来自Grafana的告警:
$.alerts[0].labels.alertname结果:来自Grafana的告警:HighCPU
关键点:
- 第一行不以
$.开头 → 作为固定文字 - 第二行以
$.开头 → 从数据中提取值 - 两行按顺序无缝拼接
对比例二:如果这里只有一行写了
$.alerts[0].labels.alertname,结果就是纯净的HighCPU;加上第一行前缀后变成来自Grafana的告警:HighCPU。
示例四:组合多个字段 + 换行排版
假设收到以下监控数据:
{
"hostname": "web-server-03",
"metric": "disk_usage",
"value": 87,
"unit": "%",
"status": "warning"
}标题字段:
磁盘警告:
$.hostname内容字段:
主机:
$.hostname
\n
指标:
$.metric
=
$.value
$.unit
\n
状态:
$.status推送结果:
- 标题:
磁盘警告: web-server-03 - 正文:
主机: web-server-03 指标: disk_usage = 87% 状态: warning
每行之间用 \n 分隔,产生换行效果。
示例五:带换行的复杂格式
同样的数据,这次让排版更清晰:
{
"alerts": [
{
"labels": { "alertname": "HighCPU", "instance": "server-01" },
"annotations": { "message": "CPU 超过 90%" }
}
]
}标题字段:
[紧急]
$.alerts[0].labels.alertname
-
$.alerts[0].labels.instance内容字段:
--- 告警详情 ---\n
$.alerts[0].annotations.message
\n--- 请及时处理 ---推送结果:
- 标题:
[紧急] HighCPU - server-01 - 正文:
--- 告警详情 --
CPU 超过 90%
--- 请及时处理 ---每一行都是独立处理的单元:
--- 告警详情 ---→ 不以$.开头,作为固定文字$.alerts[0].annotations.message→ 提取动态值--- 请及时处理 ---→ 固定文字
三者的输出按顺序无缝拼接成最终内容。
示例六:容错性演示
假设某个字段名在不同版本的 API 中可能不同,你想兼容多种可能:
{
// 版本A 的数据格式
"alert_name": "内存溢出"
}
// 或者版本B的数据格式
{
"name": "内存溢出"
}标题字段:
$.alert_name
$.name
新消息遇到版本 A 数据时:
$.alert_name→ 找到"内存溢出"→ 保留$.name→ 找不到 → 跳过新消息→ 固定文字 → 保留
结果:内存溢出新消息
遇到版本 B 数据时:
$.alert_name→ 找不到 → 跳过$.name→ 找到"内存溢出"→ 保留新消息→ 固定文字 → 保留
结果:内存溢出新消息
可以看到最后一行固定文字始终会出现在结果末尾。如果想去掉它,删除最后一行即可。
示例七:extraData 企业微信图文消息
假设第三方系统(你无法控制其结构)发来的 Webhook 数据如下:
{
"msg": {
"title": "磁盘告警",
"message": "磁盘使用率已达 98%",
"url": "https://example.com/dashboard",
"picurl": "https://example.com/alert.png"
}
}附加数据 (extraData) 输入框填写一份 JSON 模板(用 $. 占位从 payload 取值):
{
"wecom": {
"channelType": "news",
"articles": [
{
"title": "$.msg.title",
"description": "$.msg.message",
"url": "$.msg.url",
"picurl": "$.msg.picurl"
}
]
}
}标题字段 / 内容字段 仍正常填写(例如 $.msg.title、$.msg.message),也可留空由兜底机制处理。
运行时系统解析 extraData 模板,把占位符替换为实际值,得到:
{
"wecom": {
"channelType": "news",
"articles": [
{
"title": "磁盘告警",
"description": "磁盘使用率已达 98%",
"url": "https://example.com/dashboard",
"picurl": "https://example.com/alert.png"
}
]
}
}这份 JSON 整体透传给企业微信渠道,渠道端读取 wecom 命名空间,按 channelType: "news" 以图文形式发送。
要点:因为入站配置恰恰用在「无法控制请求体」的第三方 Webhook 上,传进来的数据通常没有
title/content/extraData的概念。所以extraData不是在 payload 里读取一个现成的对象,而是在配置侧写好模板,用$.xxx.yyy把散落在 payload 各处的字段拼成渠道需要的格式。
预设模板的行为说明
选择预设模板(非「通用」)时,字段映射由系统自动完成,同时会对消息做额外的内容丰富:
| 模板 | 自动追加的信息 |
|---|---|
| Grafana / Prometheus | 在正文末尾追加告警标签(如 alertname=xxx, instance=xxx)、摘要和当前状态 |
| GitHub | 在正文末尾追加操作者账号和触发的分支名 |
| Emby | 在正文末尾追加事件类型、用户名、服务器名称和级别 |
这些信息会拼接到已提取的内容后面,让你在不修改配置的情况下获得更完整的上下文。
如果不需要这些额外信息,可以选择「通用」模板自行配置纯净的字段映射。
兜底机制
当所有字段映射都无法提取到有效值时,系统有以下保障:
- 正文为空:整个原始数据的 JSON 内容将作为正文发送,确保不丢失信息
- 标题为空:自动使用默认标题
新消息 - 消息类型无效:自动回退为
text(纯文本)
调试方法
使用入站配置面板内的测试功能
入站配置面板底部自带「测试请求」区域:
- 切换不同的数据来源类型时,测试数据区会自动填充对应的示例 JSON
- 也可以粘贴你自己实际的 Webhook 数据
- 点击「发送测试请求」,消息会立即通过该接口的绑定渠道发出
- 观察收到的消息是否与你预期的格式一致
推荐调试流程
- 先用示例数据 + 你的字段映射配置 → 点测试 → 看效果
- 调整映射规则直到输出符合预期
- 保存配置后,再把入站地址填入第三方系统
- 用真实 Webhook 触发一次验证
常见问题
Q:为什么选择预设模板后看不到字段映射输入框?
这是正常设计。预设模板(Grafana、Prometheus、GitHub、Emby)已经内置了推荐的映射规则,无需手动配置。只有选择「通用」时才需要自己填写。
Q:标题或正文显示了整个 JSON?
说明字段映射没有匹配上原始数据的结构。检查以下几项:
- JSONPath 路径拼写是否正确(区分大小写)
- 数组索引是否越界
- 使用调试功能粘贴实际数据,逐步验证每条路径是否能取到值
Q:我想同时包含固定文字和动态值?
可以,但必须把它们写在不同的行。固定文字单独一行,$. 路径单独一行。不要写在同一行里。
Q:多行路径之间是什么关系?
对于同一个目标字段的多行内容:
- 以
$.开头的行:独立提取,找到值的保留,找不到的跳过 - 不以
$.开头的行:作为固定文字直接保留(\n会变成换行符,\t会变成制表符) - 所有有效行按书写顺序拼接为一个字符串
Q:如何在不同信息之间换行?
每行的输出默认无缝连接。要插入换行,有两种写法:
方法一:单独一行写 \n
主机: $.hostname
\n
指标: $.metric方法二:在文字末尾追加 \n
--- 告警详情 ---\n
$.alerts[0].annotations.message支持的转义字符:
| 输入 | 效果 |
|---|---|
\n | 换行 |
\t | 制表符(Tab 缩进) |
\\ | 反斜杠本身 |
Q:为什么我的前缀没有生效?
检查是否把前缀文字和 $. 路径写在同一行了。正确做法是分两行写:
❌ 错误:前缀文字: $.path.field
✅ 正确:
前缀文字:
$.path.fieldQ:附加数据 (extraData) 应该怎么填?
extraData 只接受一份合法 JSON 模板,而不是 JSONPath,也不是多行路径。在模板里:
- 固定不变的内容直接写原值(如
"channelType": "news"); - 需要从 Webhook 数据里取的值写成
$.xxx.yyy占位(如"title": "$.msg.title"); - 运行时系统会递归遍历整份 JSON,把所有
$.开头的字符串替换为 payload 中对应的实际值。
示例(企业微信图文):
{
"wecom": {
"channelType": "news",
"articles": [
{ "title": "$.msg.title", "description": "$.msg.message", "url": "$.msg.url" }
]
}
}如果填的不是合法 JSON,保存时会被拦截并提示,请检查格式(如是否漏了引号、逗号)。
Q:extraData 里的 $. 占位符取不到值会怎样?
该键会被保留,值替换为 null。例如 payload 中没有 msg.url 时,"url": "$.msg.url" 会变成 "url": null。如果希望整个字段不参与发送,需要在数据源侧保证对应字段存在。
Q:令牌能不能不放在 URL 里?
可以。除了默认的 POST /api/inbound/你的令牌,也支持把令牌放在 Authorization 请求头中:
POST /api/inbound
Authorization: Bearer 你的令牌这种方式避免了把令牌暴露在 URL(URL 可能被浏览器、代理、服务器日志记录),更安全。两种方式的入站解析、字段映射、推送行为完全一致。