Skip to content

API 文档

概述

BotBell API 让你的程序和脚本能够向你的 Apple 设备发送推送通知并接收回复。无需 SDK——只需简单的 HTTP 请求即可。

Base URL: https://api.botbell.app

工作流程

  1. 在 BotBell 应用中创建一个 Bot,复制其推送 URL
  2. 用任何语言或工具向推送 URL 发送 JSON 消息
  3. 在你的所有设备上即时收到推送通知
  4. 可选:通过回复回调或轮询 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)

字段类型必填说明
messagestring消息正文(最多 4,096 个字符)
titlestring显示在消息上方的标题(最多 256 个字符)
urlstring附加在通知上的可点击链接
imageUrlstring通知中展示的图片 URL

请求示例

curl
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>"
)

验证签名的步骤:

  1. 从请求头中提取 X-Webhook-Timestamp 和 X-Webhook-Signature
  2. 检查时间戳与当前时间差不超过 5 分钟(防止重放攻击)
  3. 使用 Webhook Secret 作为密钥,对字符串 "<timestamp>.<raw_body>" 计算 HMAC-SHA256
  4. 将计算结果与签名中 "sha256=" 后面的值进行比较

你的服务器响应

返回任意 HTTP 2xx 状态码表示接收成功。非 2xx 响应或超时(5 秒)将被视为投递失败——回复会存入轮询队列保留 24 小时,你可以稍后通过轮询 API 获取。


轮询获取回复

如果你没有可接收回调的公网服务器,可以改用轮询方式获取用户回复。当你的回复 URL 暂时不可用时,轮询也可以作为备用方案。

接口地址

GET https://api.botbell.app/v1/messages/poll
Headers:
  X-Bot-Token: <your_bot_token>

查询参数

字段类型默认值说明
sinceinteger-仅返回此 Unix 时间戳之后的消息
limitinteger20返回的消息条数(最大 100)

响应

{
  "code": 0,
  "message": "success",
  "data": {
    "messages": [
      {
        "message_id": "msg_xyz789",
        "content": "Got it, thanks!",
        "timestamp": 1709827300
      }
    ],
    "has_more": false
  }
}

注意: 已轮询的消息会被标记为已读,不会重复返回。未轮询的消息在 24 小时后自动过期。


错误代码

代码HTTP 状态码说明
40001401无效或缺少 Bot 令牌
40010400请求校验失败(如缺少 message 字段、请求体过大等)
40029429超出速率限制——每分钟请求数过多
40030429月度消息额度已用尽
40031403当前方案的 Bot 数量已达上限
50000500服务器内部错误

速率限制与配额

限制项
推送速率(每个 Bot)60
消息正文(字符数)4,096
标题(字符数)256
每月免费额度每 Bot 300 条消息
轮询消息保留时长24h
回复回调超时时间5s

速率限制状态会在响应头中返回:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1709827260

代码示例

发送消息

curl
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"}'
Python
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": "...", ...}}
JavaScript / Node.js
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);
Go
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
<?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);

验证回复签名

Python
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)
Node.js
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),
  );
}