跳到主要内容

发送提醒

每个频道都有一个或多个发送链接。要发送提醒,向发送链接 POST 一段 JSON 即可。完整链接可以在应用中复制,格式如下:

https://<api-host>/v1/send/<token>

链接中的 token 是唯一凭证。任何拿到链接的人都能向这个频道发送提醒,所以不要把它放进公开仓库或日志。发送链接只能发送,不能读取提醒,也不能修改频道。

终端窗口
curl -X POST "$SEND_URL" \
-H 'Content-Type: application/json' \
-d '{"alert": {"title": "构建失败", "body": "main 分支有 3 个测试失败"}}'

请求成功时返回 201 Created。提醒会在响应之前保存好,随后在后台投递到设备。

字段 类型 说明
alert.title string 必填。 提醒标题。
alert.body string 可选,显示在标题下方的详细内容。
level string urgent、high、normal 或 silent,默认 normal。见提醒级别与重复提醒。
require_ack boolean 在有人确认之前,提醒一直保持待处理。见确认与回执。
receipt boolean 即使不要求确认,也返回一个可以查询的回执。
callback boolean 提醒被确认时调用频道的确认回调。
emergency object 紧急提醒的重复提醒时间:retry_seconds 和 expire_seconds。
url string 从提醒中打开的链接。
event.type string 发生了什么,例如 build.failed。
event.subject string 发生在什么对象上,例如 repo:payments:main。
event.fingerprint string 事件的稳定标识,用来识别重复事件。默认由 subject 和 type 组成。
dedupe object 重复事件的处理方式,见重复事件。
thread_key string 把相关提醒归为一组,显示在提醒详情中。
payload object 想随提醒一起保存的任意 JSON。会保存并由接口返回,但不会显示。
expires_at string RFC 3339 时间,过了这个时间提醒可以被删除。默认为发送后 21 天。
idempotency_key string 用同一个 key 重试请求,会返回原来的提醒而不会再创建一条。也可以放在 Idempotency-Key 请求头中。

要求端到端加密的频道不接受 alert,而是接受加密信封,见加密提醒。

{
"message": {
"id": "msg_4f1c…",
"receipt_id": "rcp_9a02…",
"level": "urgent",
"require_ack": true,
"status": "open",
"dedupe_count": 1
},
"decision": { "reason": "new_message" }
}

响应中的 message 是保存下来的提醒,这里只列出了部分字段。提醒需要确认或请求了 receipt 时会有 receipt_id。decision.reason 说明这次请求的结果:

值 含义
new_message 创建了一条新提醒。
duplicate_updated 重复事件更新了一条未处理的提醒,见下文。
duplicate_recorded 记录了一次重复,但没有修改提醒。
duplicate_dropped 忽略了这次重复。
idempotent_replay 这个 idempotency_key 已经用过,返回原来的提醒。

decision.effective_policy 给出这次实际采用的重复事件处理方式,字段名与请求中的 dedupe 对象相同。

每分钟失败一次的任务不应该塞满你的收件箱。请求带有 event,并且有一条指纹相同、仍未处理、在时间窗口内出现过的提醒时,这次请求会更新那条提醒,而不是新建一条:标题、正文、链接和级别被替换,出现次数加一,应用里会显示为“出现 3 次”这样的字样。除非提醒需要确认,重复事件不会再次通知。

没有任何 event 字段的请求永远不会被当作重复事件。

字段 默认值 说明
dedupe.mode coalesce none 表示总是新建提醒。
dedupe.window_seconds 600 距上一次重复多长时间内,新的请求仍会合并到这条未处理的提醒。
dedupe.accept_policy update_visible update_visible 更新提醒;update_and_notify 更新并再次通知;record_only 只计数不修改;drop 直接忽略。
dedupe.renotify_after_seconds 0 允许再次通知时,距上次通知至少要间隔的时间。

提醒被确认或解决之后,同一指纹的下一次请求会开始一条新提醒。

长度按字符计算,一个汉字、一个英文字母、一个表情符号都算一个字符。首尾空白会被忽略。

字段 限制
请求体 64 KB
alert.title 100 个字符,只能一行
alert.body 1000 个字符,可以包含换行和制表符
alert.title + alert.body 合计 1000 个字符
url 512 字节;必须是 http 或 https 绝对地址,非 ASCII 字符需百分号编码
thread_key、idempotency_key 128 个字符
event.type 64 个字符
event.subject、event.fingerprint 200 个字符
payload JSON 不超过 8 KB,嵌套不超过 8 层

超过限制的请求会以 alert_too_long 拒绝,不会被悄悄截断。推送通知中过长的正文会被截短,但收件箱里始终是完整内容。所有限制汇总在限制。

  • 每个发送链接默认每分钟接受 60 次请求。超过后请求返回 429 limit_exceeded 和 Retry-After 响应头,且不算作已发送。
  • 如果一个发送链接远远超出限制还在持续发送——一分钟内至少 100 次被拒绝,或者限制的十倍(取较大者)——它会被暂停,之后返回 401 invalid_send_url。否则服务器无法阻止一个失控的脚本,而泄露的链接也绝不会被悄悄恢复。修好发送端之后,在应用中重新生成链接。
  • 每个网络地址在所有接口上每分钟最多 600 次请求,超过后返回 429 rate_limited。

格式校验错误会先检查,不会占用发送链接的次数。

错误响应是带有稳定 code 的 JSON:

{ "error": { "code": "alert_too_long", "message": "…", "field": "alert.title", "limit": 100, "actual": 128 } }
状态码 错误码 含义
400 invalid_json 请求体不是合法的 JSON 或 UTF-8。
401 invalid_send_url 发送链接错误、已撤销、已暂停,或所属频道已删除。
413 request_too_large 请求体超过 64 KB。
422 invalid_target 缺少 alert.title、字段值无效或含有不允许的字符,或请求在其他方面无效;field 指明字段。
422 alert_too_long 字段超过限制。错误中包含 field、limit 和 actual。
422 invalid_alert_level level 不是四种级别之一。
422 invalid_emergency emergency 的时间超出范围。
422 invalid_callback 对无法确认的提醒请求了 callback。
422 callback_not_configured 请求了 callback,但频道没有启用回调。
422 encryption_required 频道只接受端到端加密的提醒。
422 invalid_encrypted_message 加密信封不完整或格式错误。
429 limit_exceeded 达到了发送链接的频率限制。等待 Retry-After 指定的秒数后重试。
429 rate_limited 来自你的地址的请求过多。等待 Retry-After 指定的秒数后重试。

整个接口的所有错误码见错误。