Skip to content

API 文档

Echo酱推送服务对外提供 Webhook HTTP 接口,用于消息推送、决策任务投递和 Robot 接入。

接入地址

https://echo.dorimu.cn

目录


通用约定

响应格式

所有接口统一返回以下 JSON 结构:

json
// 成功(有结果)
{ "code": 200, "result": { ... } }

// 成功(空响应)
{ "code": 200 }

// 失败
{ "code": 401, "message": "未授权访问" }

错误码

code含义
400参数错误 / 校验失败
401未认证 / token 无效
403无权限
404资源不存在
409资源冲突
429限流
500内部错误

消息推送

POST /push/{push_id}

通过渠道的 push_id 推送消息到绑定的设备。push_id 可在 管理后台 创建渠道后获取。

鉴权策略:

  • 当渠道 require_signature = true 时,必须携带签名头(见下方签名计算规则)
  • 当渠道 require_signature = false 时,无需签名即可推送

请求体:

字段类型必填说明
formatstring消息格式,默认 normal。可选:normal / image / button
titlestring消息标题,最长 120 字符
descriptionstring消息摘要,最长 256 字符
contentstringnormal 时必填消息内容,最长 4000 字符
image_urlstringimage 时必填图片地址,必须为 https
buttonsarraybutton 时必填按钮列表,1-5 项,每项含 texturl

示例 — 普通文本消息:

bash
curl -X POST https://echo.dorimu.cn/push/{push_id} \
  -H "Content-Type: application/json" \
  -d '{
    "title": "服务器告警",
    "content": "CPU 使用率超过 90%"
  }'

示例 — 图片消息:

json
{
  "format": "image",
  "title": "监控截图",
  "image_url": "https://example.com/screenshot.png"
}

示例 — 按钮消息:

json
{
  "format": "button",
  "title": "构建完成",
  "description": "main 分支构建 #128 成功",
  "buttons": [
    { "text": "查看详情", "url": "https://ci.example.com/builds/128" },
    { "text": "查看日志", "url": "https://ci.example.com/builds/128/logs" }
  ]
}

返回示例:

json
{
  "code": 200,
  "result": {
    "message_id": "uuid",
    "status": "queued"
  }
}

签名计算规则

当渠道开启签名校验时,请求需携带以下 Header:

Header说明
X-Timestamp当前 Unix 秒
X-Signature-256sha256=<hex>

计算方式:

  1. 拼接原文:<timestamp>.<raw_body>
  2. 使用渠道密钥计算:HMAC-SHA256(secret, 原文)
  3. 设置头值:sha256=<hex结果>

时间容忍窗口为 5 分钟,超出将被拒绝。


签名凭证管理

渠道签名密钥通过以下接口管理。鉴权需要在 Header 中携带 Authorization: Bearer <token>,token 为登录后获取的会话令牌或在管理后台创建的 PAT(pf_ 前缀)。仅渠道所有者可操作。

POST /push/{push_id}/credentials

创建或轮换该渠道的签名密钥。

json
{
  "code": 200,
  "result": {
    "secret": "hex-encoded-secret"
  }
}

注意

secret 仅创建时返回一次,请妥善保存。丢失后只能重新轮换生成新密钥。

DELETE /push/{push_id}/credentials

撤销该渠道的活动凭证。撤销后,若渠道 require_signature = true,推送请求将返回 401

json
{
  "code": 200,
  "message": "credential revoked"
}

决策任务

决策任务用于创建需要用户确认/审批的交互场景(如发布审批、告警确认)。流程:

  1. 通过 Webhook 创建决策任务 → 用户在客户端收到通知
  2. 用户做出选择 → 任务状态变更
  3. 你的服务通过轮询、长轮询或 WebSocket 获取结果

POST /push/{push_id}/decision

创建决策任务,任务自动归属到该渠道的所有者。

鉴权: 同消息推送接口,按渠道配置决定是否要求签名。

请求体:

字段类型必填说明
titlestring任务标题,1-255 字符
descriptionstring任务描述
optionsarray选项列表,2-10 项
options[].keystring选项标识,1-50 字符
options[].labelstring选项显示文本,1-100 字符
options[].descriptionstring选项描述
default_policystring超时策略,默认 auto_reject。可选:auto_approve / auto_reject / escalate
expires_in_secondsint64过期时间(秒),60-600
idempotency_keystring幂等键,最长 64 字符

示例:

bash
curl -X POST https://echo.dorimu.cn/push/{push_id}/decision \
  -H "Content-Type: application/json" \
  -d '{
    "title": "是否执行生产发布",
    "description": "main 分支 #128 即将部署到生产环境",
    "options": [
      { "key": "approve", "label": "批准发布" },
      { "key": "reject", "label": "拒绝" }
    ],
    "default_policy": "auto_reject",
    "expires_in_seconds": 300
  }'

返回示例:

json
{
  "code": 200,
  "result": {
    "task_id": "uuid",
    "state": "pending",
    "expires_at": "2026-02-22T10:00:00Z",
    "created_at": "2026-02-22T09:55:00Z"
  }
}

GET /push/{push_id}/decision/{task_id}

查询单个决策任务当前状态。

返回字段:

字段类型说明
task_idstring决策任务 ID
titlestring任务标题
statestring状态:pending / decided / expired / escalated
default_policystring超时策略
decision_keystring用户选择的选项 key(未决时为空)
decided_bystring决策者 ID(未决时为空)
decided_atstring决策时间(RFC3339,可选)
expires_atstring过期时间(RFC3339)
created_atstring创建时间(RFC3339)
is_finalbool是否为终态

GET /push/{push_id}/decision/{task_id}/wait

长轮询等待决策任务状态变化。适合不方便使用 WebSocket 的场景。

Query 参数:

字段类型必填说明
timeoutint等待超时(秒),默认 25,最大 30

行为:

  • 窗口内状态变化时立即返回
  • 无变化则超时返回当前快照
  • 任务已是终态时立即返回

返回字段: 包含上方查询接口的所有字段,额外新增:

字段类型说明
changedbool本次请求期间是否发生变化
wait_timeoutbool是否等待超时

GET /push/{push_id}/decision/ws

建立决策专用 WebSocket,实时等待任务结果。适合需要即时响应的场景。

Query 参数:

字段类型必填说明
task_idstring决策任务 ID

连接行为:

  • 建连后立即推送 decision.snapshot(当前状态快照)
  • 状态变化时推送 decision.updated
  • 每 25 秒推送 heartbeat 保活
  • 单连接最长 10 分钟,超时前推送 decision.timeout 后断开

帧格式:

json
// 状态更新
{"type": "decision.updated", "result": {"task_id": "...", "state": "decided", "decision_key": "approve"}}

// 心跳
{"type": "heartbeat", "timestamp": 1708677300123}

// 超时断开
{"type": "decision.timeout", "result": {"task_id": "...", "state": "expired"}}

Robot 接入

Robot 是 Echo酱的聊天机器人能力。接入流程:

  1. 管理后台 或通过客户端创建 Robot,获取 robot_idendpoint_secret
  2. Robot 主动连接服务端(WebSocket / SSE / 长轮询)接收用户消息
  3. Robot 处理后回传结果(回执或主动消息)

鉴权方式

所有 Robot 接口使用签名鉴权,需在请求头中携带:

Header说明
X-Robot-IDRobot ID
X-Timestamp当前 Unix 秒(容忍窗口 60 秒)
X-Nonce随机字符串(10 分钟内不可重复)
X-Signature-256sha256=<hex>

签名计算:

  1. 拼接原文:<timestamp>.<nonce>.<METHOD>.<path>.<sha256(raw_body)>
  2. 计算:HMAC-SHA256(endpoint_secret, 原文)
  3. 设置头值:sha256=<hex结果>

注意事项:

  • METHOD 使用 HTTP 大写方法名(GETPOST
  • path 仅路径部分,不含 query string
  • GET 请求的 raw_body 为空字节串,其 SHA256 固定为 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
  • 每次请求必须使用新的 nonce

连接方式

Robot 有三种连接方式,按推荐顺序:

方式接口说明
WebSocket(推荐)GET /robot/ws双向通信,任务推送 + 回执均在 WS 内完成
SSEGET /robot/events单向接收任务,回执通过 POST /robot/reply
长轮询GET /robot/poll拉取单条任务,回执通过 POST /robot/reply

GET /robot/poll 支持 timeout 参数(秒,默认 20,最大 30)。

任务下发体

当用户发送消息或点击卡片按钮时,Robot 会收到以下结构的任务:

字段类型说明
job_idstring任务唯一 ID(回执时必须回传)
session_idstring会话 ID
message_idstring触发消息 ID
robot_idstringRobot ID
user_idstring用户 ID
contentstring消息内容
typestring任务类型:chat.message(用户消息)/ action_clicked(按钮点击)
action_idstring按钮 ID(仅 action_clicked
action_valuestring按钮附加数据(仅 action_clicked
target_message_idstring被点击的卡片消息 ID(仅 action_clicked
created_atstring创建时间(RFC3339)

POST /robot/reply

Robot 回执上报(配合 SSE / Poll 模式使用,WS 模式直接在连接内发送 type=reply)。

请求体:

字段类型必填说明
job_idstring对应任务的 job_id
contentstring回复内容
is_completebool是否为完整回复(流式输出时先发 false,最后一条发 true
content_typestring默认 text。可选:text / markdown / card / ocard / pass
actionstring默认 append。可选:append / edit
target_message_idstringedit 时必填要编辑的目标消息 ID

POST /robot/messages

Robot 主动发送消息,不依赖用户先发消息。

请求体:

字段类型必填说明
session_idstring会话 ID
actionstring默认 append。可选:append(追加)/ edit(编辑)/ recall(撤回)
target_message_idstringedit / recall 时必填目标消息 ID
contentstringappend / edit 时必填消息内容
content_typestring默认 text。可选:text / markdown / card / ocard
is_completebool默认 true

返回示例:

json
{
  "code": 200,
  "result": {
    "message_id": "uuid",
    "status": "queued"
  }
}

GET /robot/sessions

查询当前 Robot 关联的会话列表。

Query 参数:

字段类型必填说明
statestring默认 active,可传 archived 查看归档会话
pageint页码,默认 1
limitint每页数量,默认 20,最大 100

返回字段:

字段类型说明
sessions[].session_idstring会话 ID
sessions[].user_idstring用户 ID
sessions[].titlestring会话标题
sessions[].statestring会话状态
sessions[].last_active_atstring最后活跃时间
sessions[].created_atstring创建时间
totalint总数

GET /robot/history

查询指定会话的历史消息,用于 Robot 获取对话上下文。

Query 参数:

字段类型必填说明
session_idstring会话 ID
limitint返回条数,默认 30,最大 30

返回字段:

字段类型说明
messages[].message_idstring消息 ID
messages[].sender_typestring发送者类型:user / robot
messages[].content_typestring内容类型:text / markdown / card / ocard
messages[].contentstring消息内容
messages[].sequence_numint64消息序号
messages[].is_completebool是否完整消息
messages[].recalledbool是否已撤回
messages[].parent_idstring父消息 ID(Robot 回复时指向用户原始消息)
messages[].created_atstring创建时间(RFC3339)

内容类型说明

Robot 回复支持以下内容类型(content_type):

说明使用方
text纯文本(默认)用户、Robot
markdownMarkdown 格式文本,客户端负责渲染用户、Robot
card卡片 JSON,支持结构化内容和交互按钮仅 Robot
ocard一次性卡片,用户点击按钮后自动撤回该消息仅 Robot
pass确认收到但不回复,不创建消息、不推送通知仅 Robot(回执)

Card JSON 格式

cardocardcontent 字段为 JSON 字符串:

json
{
  "title": "审批单",
  "body": "请审批发布 #1024(支持 Markdown)",
  "actions": [
    { "id": "approve", "label": "通过", "style": "primary", "value": "1024" },
    { "id": "reject", "label": "驳回", "style": "danger", "value": "1024" }
  ]
}
字段类型必填说明
titlestring卡片标题
bodystring卡片正文,支持 Markdown
actionsarray交互按钮列表
actions[].idstring按钮唯一标识,对应 action_clicked 任务中的 action_id
actions[].labelstring按钮显示文本
actions[].stylestring按钮样式:primary / danger / default
actions[].valuestring按钮携带的附加数据,对应 action_value

用户点击按钮后,Robot 会收到 type=action_clicked 的任务,包含 action_idaction_value。若消息类型为 ocard,按钮点击后该消息会被自动撤回。