Skip to content

API 문서

개요

BotBell API를 사용하면 프로그램과 스크립트에서 Apple 기기로 푸시 알림을 보내고 답장을 받을 수 있습니다. SDK가 필요 없습니다 — 간단한 HTTP 요청만으로 충분합니다.

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

작동 방식

  1. BotBell 앱에서 봇을 만들고 푸시 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"
}

메시지 전송

봇에서 BotBell 앱으로 알림을 보냅니다. 봇의 푸시 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

요청 예시

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을 붙여넣고 기기로 실제 푸시 알림을 보내보세요.


답장 수신

앱에서 봇에 답장하면 BotBell이 자동으로 답장을 서버에 전달할 수 있습니다. 이 기능은 선택 사항이며 — 봇이 양방향 대화를 처리하는 경우에만 필요합니다.

답장 URL 설정

BotBell 앱에서 봇 설정으로 이동하여 답장 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
}

서명 검증

각 콜백에는 BotBell에서 보낸 것인지 확인할 수 있도록 HMAC-SHA256 서명이 포함됩니다. 서명 키는 봇 설정에 표시된 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
      }
    ],
    "has_more": false
  }
}

참고: 폴링된 메시지는 읽음으로 표시되며 다시 반환되지 않습니다. 폴링되지 않은 메시지는 24시간 후 만료됩니다.


오류 코드

코드HTTP 상태설명
40001401유효하지 않거나 누락된 봇 토큰
40010400요청 유효성 검사 실패 (예: message 필드 누락, 본문이 너무 큼)
40029429속도 제한 초과 — 분당 요청 수 초과
40030429월간 메시지 할당량 소진
40031403플랜의 봇 개수 한도 도달
50000500내부 서버 오류

속도 제한 및 할당량

제한
푸시 속도 (봇당)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),
  );
}