Back to home

Open API

For third-party integrations: pull appointment data, or receive push notifications when new appointments are created.

API Documentation

Overview

The Open API offers two mechanisms: (1) Pull — a third party calls the HTTP API to fetch appointment data by filters; (2) Push — when a new appointment is created, the system pushes an appointment.created event to the notification URL you configured. Both share the same HMAC-SHA256 signature. Credentials have two parts: the API Key identifies you and is sent in plain text in request headers, while the API Secret is used to compute the signature and never appears in the request payload. Resetting the secret only changes the API Secret; the API Key stays the same.

Pull API

Endpoint
GET https://phone-api.bookmi.ai/api/open/v1/appointments

Request headers

NameDescription
X-Api-KeyIdentity API Key (starts with ak_)
X-TimestampUnix seconds timestamp (string); must be within ±300 seconds of server time
X-SignatureHMAC-SHA256 signature of this request (lowercase hex)

Signature algorithm

The string to sign is four lines joined by newlines: line 1 is the timestamp, line 2 the HTTP method (GET), line 3 the request path /api/open/v1/appointments, line 4 the query string. The query string must match what you actually send byte for byte — no reordering, no re-encoding (empty string when there are no parameters). Compute HMAC-SHA256 over this string using the API Secret as the key and use the lowercase hex as X-Signature. The timestamp must be Unix seconds within ±300 seconds of server time, otherwise it is treated as expired.

Query parameters

NameDescription
updated_sinceReturn only appointments updated after this time (ISO 8601)
scheduled_fromLower bound of scheduled time (ISO 8601)
scheduled_toUpper bound of scheduled time (ISO 8601)
statusFilter by status: pending / confirmed / cancelled
pagePage number, starting at 1 (default 1)
page_sizeItems per page, 1-200 (default 50)

Response example

{
  "code": 0,
  "message": "ok",
  "data": {
    "total": 128,
    "items": [
      {
        "id": 1024,
        "scheduled_at": "2026-07-20T10:00:00+09:00",
        "party_size": 2,
        "contact_name": "山田太郎",
        "contact_phone": "+819012345678",
        "subject": "カット予約",
        "note": null,
        "status": "confirmed",
        "outside_slot": false,
        "item_name": "カット",
        "item_external_id": "svc_001",
        "created_at": "2026-07-16T12:34:56+09:00",
        "updated_at": "2026-07-16T12:34:56+09:00"
      }
    ]
  }
}

Field reference

FieldDescription
idAppointment primary key
scheduled_atScheduled time (nullable)
party_sizeParty size / quantity (nullable)
contact_nameContact name (nullable)
contact_phoneContact phone (nullable)
subjectAppointment subject
noteNote (nullable)
statusStatus: pending / confirmed / cancelled
outside_slotWhether it falls in an unbookable slot (closed / out of hours / full)
item_nameItem name snapshot (nullable)
item_external_idThird-party item ID snapshot; set only on an exact match to a merchant item (nullable)
created_atCreated time
updated_atUpdated time

Push notifications

Once a notification URL is set, whenever a new appointment is created (via AI extraction or manual entry) the system sends one POST request to that URL with the JSON body below.

Push body example

{
  "event": "appointment.created",
  "delivery_id": 8801,
  "timestamp": 1752633600,
  "data": {
    "id": 1024,
    "scheduled_at": "2026-07-20T10:00:00+09:00",
    "party_size": 2,
    "contact_name": "山田太郎",
    "contact_phone": "+819012345678",
    "subject": "カット予約",
    "note": null,
    "status": "pending",
    "outside_slot": false,
    "item_name": "カット",
    "item_external_id": "svc_001",
    "created_at": "2026-07-16T12:34:56+09:00",
    "updated_at": "2026-07-16T12:34:56+09:00"
  }
}

Push signature

Push requests carry X-Timestamp and X-Signature headers: the string to sign is timestamp + a period + the raw request body bytes, computed with HMAC-SHA256 using the API Secret as the key. Your service should recompute and compare the signature, processing only on a match. Return 2xx to mark the delivery as successful.

Retry and idempotency

A failed delivery (non-2xx or timeout) is retried automatically at intervals of 1 minute, 5 minutes, 30 minutes, 2 hours, and 6 hours — 6 attempts including the first — after which it becomes a terminal state and is no longer delivered. Repeated deliveries for the same appointment carry the same delivery_id; use it to deduplicate and stay idempotent. When you first configure a test push, a probe event with event webhook.test (empty data object) is sent first.

Error codes

Beyond the HTTP status, business errors return the following business codes in the response body's code field.

CodeHTTPMeaning
1801403Open API feature is not enabled
1802401Invalid API Key
1803401Invalid signature or expired timestamp
1804400Invalid notification URL
1805400Generate credentials and set a notification URL first

Code examples

Pull (curl)

# 拉取预约(签名 = HMAC-SHA256(secret, ts + "\n" + "GET" + "\n" + path + "\n" + query))
TS=$(date +%s)
QUERY="page=1&page_size=50"
SIG=$(printf '%s\nGET\n/api/open/v1/appointments\n%s' "$TS" "$QUERY" \
  | openssl dgst -sha256 -hmac "$API_SECRET" -hex | awk '{print $NF}')
curl "https://phone-api.bookmi.ai/api/open/v1/appointments?$QUERY" \
  -H "X-Api-Key: ak_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -H "X-Timestamp: $TS" -H "X-Signature: $SIG"

Push verification (Python)

# 推送验签(收到 POST 时)
import hashlib, hmac
def verify(secret: str, timestamp: str, body: bytes, signature: str) -> bool:
    expected = hmac.new(secret.encode(), timestamp.encode() + b"." + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)