Skip to content

API Reference

概要

BotBell API を使えば、プログラムやスクリプトから Apple デバイスにプッシュ通知を送信し、返信を受け取ることができます。SDK は不要——シンプルな HTTP リクエストだけで利用できます。

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

仕組み

  1. BotBell アプリで Bot を作成し、プッシュ URL をコピーします
  2. 任意の言語やツールからプッシュ URL に JSON メッセージを POST します
  3. すべてのデバイスで即座にプッシュ通知を受け取ります
  4. オプションとして、返信コールバック URL またはポーリング API であなたの返信を受け取れます

レスポンス形式

すべての 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/pushX-Bot-Token: <token> ヘッダーを付けて使用することもできます。

リクエストボディ(JSON)

フィールド必須説明
messagestringはいメッセージ本文(最大 4,096 文字)
titlestringいいえメッセージの上に表示されるタイトル(最大 256 文字)
urlstringいいえ通知に添付されるタップ可能なリンク
imageUrlstringいいえ通知に表示する画像の URL
actionsarrayいいえQuick reply buttons (max 5). See Actions below.

Action Fields

フィールド必須説明
keystringはいAction identifier returned when user taps (max 64 chars)
labelstringはいButton text displayed to user (max 64 chars)
typestringいいえ"button" (default) sends label as reply; "input" opens a text field for custom input
placeholderstringいいえPlaceholder text for input field (max 128 chars, only for type "input")
Actions Example
{
  "message": "Deploy v2.3.0 to production?",
  "title": "Deploy Request",
  "actions": [
    {"key": "approve", "label": "Approve"},
    {"key": "reject", "label": "Reject"},
    {"key": "custom", "label": "Other...", "type": "input", "placeholder": "Enter your reason"}
  ]
}

リクエスト例

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
deliveredAPNs へのプッシュ通知送信が成功した場合は true、登録済みデバイスがない場合は false
timestampメッセージが作成された Unix タイムスタンプ(秒)

今すぐ試す

Bot Token を貼り付けて、デバイスにリアルなプッシュ通知を送信しましょう。


返信の受信

アプリで Bot に返信すると、BotBell はその返信を自動的にサーバーに転送できます。これはオプションです——Bot が双方向の会話を処理する場合にのみ必要です。

返信コールバック URL の設定

BotBell アプリで Bot の設定に移動し、返信コールバック URL(例:https://your-server.com/botbell/reply)を入力してください。BotBell はあなたの返信をリアルタイムでこの URL に 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,
  "action": "approve"
}

署名検証

各コールバックには 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 時間保存され、後で取得できます。


返信のポーリング

コールバックを受信するための公開サーバーがない場合、代わりに返信をポーリングで取得できます。返信コールバック 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,
        "action": "approve"
      }
    ],
    "has_more": false
  }
}

注意: ポーリングされたメッセージは既読としてマークされ、再度返されることはありません。ポーリングされなかったメッセージは 24 時間後に期限切れになります。


エラーコード

コードHTTP ステータス説明
40001401Bot トークンが無効または欠落しています
40010400リクエストの検証に失敗しました(例:message フィールドの欠落、ボディが大きすぎる)
40029429レート制限を超過しました——1 分あたりのリクエスト数が多すぎます
40030429月間メッセージクォータを使い果たしました
40031403プランの Bot 数上限に達しました
50000500内部サーバーエラー

レート制限とクォータ

制限
プッシュレート(Bot ごと)60
メッセージ本文(文字数)4,096
タイトル(文字数)256
月間無料枠アカウントあたり 300 メッセージ
ポーリングメッセージの保持期間24h
返信コールバック URL のタイムアウト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),
  );
}