API 文档
概述
BotBell API 让你的程序和脚本能够向你的 Apple 设备发送推送通知并接收回复。无需 SDK——只需简单的 HTTP 请求即可。
Base URL: https://api.botbell.app工作流程
- 在 BotBell 应用中创建一个 Bot,复制其推送 URL
- 用任何语言或工具向推送 URL 发送 JSON 消息
- 在你的所有设备上即时收到推送通知
- 可选:通过回复回调或轮询 API 接收你在 App 中的回复
响应格式
所有 API 响应使用统一的 JSON 信封格式。code 为 0 表示成功,其他值表示错误。
// Success
{
"code": 0,
"message": "success",
"data": { ... }
}
// Error
{
"code": 40001,
"message": "Invalid bot token"
}推送消息
从你的 Bot 向 BotBell 应用发送通知。Bot 的推送 URL 已包含认证令牌——直接 POST 即可。
接口地址
POST https://api.botbell.app/v1/push/<your_bot_token>无需 Authorization 请求头。URL 中的令牌就是你的认证凭证。
你也可以使用 POST /v1/messages/push 并携带 X-Bot-Token: <token> 请求头。
请求体(JSON)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
message | string | 是 | 消息正文(最多 4,096 个字符) |
title | string | 否 | 显示在消息上方的标题(最多 256 个字符) |
url | string | 否 | 附加在通知上的可点击链接 |
imageUrl | string | 否 | 通知中展示的图片 URL |
请求示例
curl -X POST https://api.botbell.app/v1/push/bt_your_token \
-H "Content-Type: application/json" \
-d '{
"message": "Server CPU is at 95%!",
"title": "Alert",
"url": "https://grafana.example.com/dashboard"
}'响应
{
"code": 0,
"message": "success",
"data": {
"message_id": "msg_abc123",
"delivered": true,
"timestamp": 1709827200
}
}| 字段 | 说明 |
|---|---|
message_id | 此消息的唯一 ID |
delivered | 如果推送通知已成功发送到 APNs 则为 true;如果没有已注册的设备则为 false |
timestamp | 消息创建时的 Unix 时间戳(秒) |
在线试用
粘贴你的 Bot Token,直接向你的设备发送一条真实推送通知。
接收回复
当你在 App 中回复 Bot 时,BotBell 可以自动将回复转发到你的服务器。这是可选功能——只有在你的 Bot 程序需要处理双向对话时才需要配置。
设置回复回调地址
在 BotBell 应用中,进入 Bot 设置页,输入一个回复 URL(例如 https://your-server.com/botbell/reply)。你回复时 BotBell 会即时 POST 到此地址。
回调负载
当你回复时,BotBell 会向你的回复 URL 发送以下格式的 POST 请求:
POST https://your-server.com/botbell/reply
Headers:
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Webhook-Timestamp: 1709827300
Body:
{
"event": "user_reply",
"bot_id": "bot_abc123",
"message_id": "msg_xyz789",
"content": "Got it, thanks!",
"timestamp": 1709827300
}签名验证
每次回调都包含 HMAC-SHA256 签名,用于验证请求确实来自 BotBell。签名密钥是 Bot 设置中展示的 Webhook Secret。
signature = HMAC-SHA256(
key: <your_webhook_secret>,
message: "<timestamp>.<raw_request_body>"
)验证签名的步骤:
- 从请求头中提取 X-Webhook-Timestamp 和 X-Webhook-Signature
- 检查时间戳与当前时间差不超过 5 分钟(防止重放攻击)
- 使用 Webhook Secret 作为密钥,对字符串 "<timestamp>.<raw_body>" 计算 HMAC-SHA256
- 将计算结果与签名中 "sha256=" 后面的值进行比较
你的服务器响应
返回任意 HTTP 2xx 状态码表示接收成功。非 2xx 响应或超时(5 秒)将被视为投递失败——回复会存入轮询队列保留 24 小时,你可以稍后通过轮询 API 获取。
轮询获取回复
如果你没有可接收回调的公网服务器,可以改用轮询方式获取用户回复。当你的回复 URL 暂时不可用时,轮询也可以作为备用方案。
接口地址
GET https://api.botbell.app/v1/messages/poll
Headers:
X-Bot-Token: <your_bot_token>查询参数
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
since | integer | - | 仅返回此 Unix 时间戳之后的消息 |
limit | integer | 20 | 返回的消息条数(最大 100) |
响应
{
"code": 0,
"message": "success",
"data": {
"messages": [
{
"message_id": "msg_xyz789",
"content": "Got it, thanks!",
"timestamp": 1709827300
}
],
"has_more": false
}
}注意: 已轮询的消息会被标记为已读,不会重复返回。未轮询的消息在 24 小时后自动过期。
错误代码
| 代码 | HTTP 状态码 | 说明 |
|---|---|---|
| 40001 | 401 | 无效或缺少 Bot 令牌 |
| 40010 | 400 | 请求校验失败(如缺少 message 字段、请求体过大等) |
| 40029 | 429 | 超出速率限制——每分钟请求数过多 |
| 40030 | 429 | 月度消息额度已用尽 |
| 40031 | 403 | 当前方案的 Bot 数量已达上限 |
| 50000 | 500 | 服务器内部错误 |
速率限制与配额
| 限制项 | 值 |
|---|---|
| 推送速率(每个 Bot) | 60 |
| 消息正文(字符数) | 4,096 |
| 标题(字符数) | 256 |
| 每月免费额度 | 每 Bot 300 条消息 |
| 轮询消息保留时长 | 24h |
| 回复回调超时时间 | 5s |
速率限制状态会在响应头中返回:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1709827260代码示例
发送消息
curl -X POST https://api.botbell.app/v1/push/bt_your_token \
-H "Content-Type: application/json" \
-d '{"message":"Server CPU at 95%","title":"Alert"}'import requests
resp = requests.post(
"https://api.botbell.app/v1/push/bt_your_token",
json={
"message": "Build #42 succeeded",
"title": "CI/CD",
"url": "https://ci.example.com/builds/42",
},
)
print(resp.json()) # {"code": 0, "data": {"message_id": "...", ...}}const resp = await fetch("https://api.botbell.app/v1/push/bt_your_token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
message: "New order received!",
title: "E-Commerce",
}),
});
const data = await resp.json();
console.log(data);package main
import (
"bytes"
"encoding/json"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]string{
"message": "Disk usage above 90%",
"title": "Alert",
})
http.Post(
"https://api.botbell.app/v1/push/bt_your_token",
"application/json",
bytes.NewReader(body),
)
}<?php
$ch = curl_init('https://api.botbell.app/v1/push/bt_your_token');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'message' => 'Payment received: $49.99',
'title' => 'Stripe',
]),
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
curl_close($ch);验证回复签名
import hmac, hashlib
def verify_signature(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
message = f"{timestamp}.{payload.decode()}"
expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)const crypto = require("crypto");
function verifySignature(payload, timestamp, signature, secret) {
const message = `${timestamp}.${payload}`;
const expected = crypto.createHmac("sha256", secret).update(message).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(`sha256=${expected}`),
Buffer.from(signature),
);
}