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
GET https://phone-api.bookmi.ai/api/open/v1/appointments
Request headers
| Name | Description |
|---|---|
| X-Api-Key | Identity API Key (starts with ak_) |
| X-Timestamp | Unix seconds timestamp (string); must be within ±300 seconds of server time |
| X-Signature | HMAC-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
| Name | Description |
|---|---|
| updated_since | Return only appointments updated after this time (ISO 8601) |
| scheduled_from | Lower bound of scheduled time (ISO 8601) |
| scheduled_to | Upper bound of scheduled time (ISO 8601) |
| status | Filter by status: pending / confirmed / cancelled |
| page | Page number, starting at 1 (default 1) |
| page_size | Items 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
| Field | Description |
|---|---|
| id | Appointment primary key |
| scheduled_at | Scheduled time (nullable) |
| party_size | Party size / quantity (nullable) |
| contact_name | Contact name (nullable) |
| contact_phone | Contact phone (nullable) |
| subject | Appointment subject |
| note | Note (nullable) |
| status | Status: pending / confirmed / cancelled |
| outside_slot | Whether it falls in an unbookable slot (closed / out of hours / full) |
| item_name | Item name snapshot (nullable) |
| item_external_id | Third-party item ID snapshot; set only on an exact match to a merchant item (nullable) |
| created_at | Created time |
| updated_at | Updated 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.
| Code | HTTP | Meaning |
|---|---|---|
| 1801 | 403 | Open API feature is not enabled |
| 1802 | 401 | Invalid API Key |
| 1803 | 401 | Invalid signature or expired timestamp |
| 1804 | 400 | Invalid notification URL |
| 1805 | 400 | Generate 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)