Overview

V1-compatible external API

The external API is available to merchants whose plan enables external API access and whose account is in good standing. Every request uses a bearer API key plus the merchant ID header.

Auth requiredOne active keyIdempotent create

auth

Authentication

Requests must use a bearer API key and the merchant ID header. The same key is bound to exactly one merchant.

Headers

Authorization: Bearer buqq_live_...
X-Merchant-Id: 550e8400-e29b-41d4-a716-446655440000
PropertyRequirementNotes
AuthorizationRequiredBearer <api_key>. Reject missing/invalid/revoked keys with INVALID_API_KEY.
X-Merchant-IdRequiredMerchant UUID. Must match the key's merchant or MERCHANT_MISMATCH is returned.
Idempotency-KeyPOST /bookings onlyRequired for booking creation. Reusing a key with a different body returns IDEMPOTENCY_CONFLICT.

errors

Errors

All error codes returned by the external API. Auth errors run before business logic and apply to every endpoint.

CodeStatusEndpointDescription
INVALID_API_KEY401All endpointsMissing, invalid, or revoked API key or X-Merchant-Id header.
MERCHANT_MISMATCH403All endpointsAPI key does not belong to the merchant in X-Merchant-Id.
PLAN_NOT_ENTITLED423All endpointsMerchant plan does not include external API access.
BILLING_SUSPENDED423All endpointsMerchant billing is suspended.
INTERNAL_ERROR500All endpointsUnexpected server failure.
VALIDATION_ERROR400All endpointsRequest body or query params failed validation. Includes a field-level errors array.
MISSING_IDEMPOTENCY_KEY400POST /bookingsIdempotency-Key header missing.
CONFLICT409POST /bookingsIdempotency-Key was already used with a different request body.
IDEMPOTENCY_CONFLICT409POST /bookingsSame Idempotency-Key is already being processed - retry in a few seconds.
ADVANCE_TIME_REQUIRED400POST /bookingsBooking time is within the restaurant's minimum advance window.
BOOKING_LIMIT_REACHED403POST /bookingsMonthly booking cap for this restaurant has been reached.
CAPACITY_EXCEEDED400POST /bookingsRequested slot does not have sufficient capacity for this party size.
RESTAURANT_UNAVAILABLE400POST /bookingsRestaurant is closed or not accepting bookings at the requested time.
DUPLICATE_BOOKING409POST /bookingsA booking with the same guest contact (email or phone), time, and party size was created in the last 5 minutes.
NOT_FOUND404PATCH /bookings/:id/statusBooking does not exist.

availability

GET /api/external/v1/availability

Returns available start times for a restaurant on a specific date.

GET/api/external/v1/availabilityBearer token + X-Merchant-Id
PropertyRequirementNotes
dateRequired query paramYYYY-MM-DD local restaurant date.
partySizeRequired query paramInteger >= 1. Used for capacity calculations. Must be within the restaurant's minPartySize–maxPartySize range to find available slots.

Example request (cURL)

curl -X GET "https://buqq.online/api/external/v1/availability?date=2026-05-18&partySize=4" \
  -H "Authorization: Bearer buqq_live_..." \
  -H "X-Merchant-Id: 550e8400-e29b-41d4-a716-446655440000"

Example request (JavaScript)

const res = await fetch(
  `https://buqq.online/api/external/v1/availability?date=2026-05-18&partySize=4`,
  {
    headers: {
      "Authorization": "Bearer buqq_live_...",
      "X-Merchant-Id": "550e8400-e29b-41d4-a716-446655440000",
    },
  }
);
const { availableSlots } = await res.json();

Success response 200

{
  "availableSlots": ["18:00", "19:00", "20:00"],
  "fullSlots": ["17:00", "17:30"],
  "timezone": "Asia/Bangkok",
  "minPartySize": 1,
  "maxPartySize": 10
}
PropertyRequirementNotes
availableSlotsstring[]HH:MM start times available for the requested party size on that date.
fullSlotsstring[]HH:MM slots that exist on this date but are at full capacity for the requested party size. Useful for showing greyed-out times in a picker.
timezonestringIANA timezone identifier for the restaurant (e.g. Asia/Bangkok).
minPartySizeintegerSmallest party size the restaurant accepts. Use this to validate booking form inputs before calling POST /bookings.
maxPartySizeintegerLargest party size the restaurant accepts. Use this to validate booking form inputs before calling POST /bookings.

bookings get

GET /api/external/v1/bookings

Lists bookings for a merchant across a date range with cursor pagination.

GET/api/external/v1/bookingsBearer token + X-Merchant-Id
PropertyRequirementNotes
startDateRequired query paramYYYY-MM-DD, inclusive start of the date range.
endDateRequired query paramYYYY-MM-DD, inclusive end of the date range.
statusOptional query paramComma-separated statuses: pending, checked-in, cancelled, no-show.
cursorOptional query paramLast booking id from the previous page.
limitOptional query paramDefault 50, max 200.

Example request (cURL)

curl -X GET "https://buqq.online/api/external/v1/bookings?startDate=2026-05-18&endDate=2026-05-18" \
  -H "Authorization: Bearer buqq_live_..." \
  -H "X-Merchant-Id: 550e8400-e29b-41d4-a716-446655440000"

Example request (JavaScript)

const res = await fetch(
  `https://buqq.online/api/external/v1/bookings?startDate=2026-05-18&endDate=2026-05-18`,
  {
    headers: {
      "Authorization": "Bearer buqq_live_...",
      "X-Merchant-Id": "550e8400-e29b-41d4-a716-446655440000",
    },
  }
);
const { bookings, pagination } = await res.json();

Success response 200

{
  "bookings": [
    {
      "id": "4c4a04e4-3e3d-4db7-b6f5-0e2e6d5bb7d4",
      "bookingRef": "PW-1001",
      "customerName": "Jane Doe",
      "phone": "+85512345678",
      "email": "jane@example.com",
      "partySize": 4,
      "bookingTimeUtc": "2026-05-18T11:00:00.000Z",
      "localDate": "2026-05-18",
      "localTime": "18:00",
      "status": "pending",
      "source": "pos",
      "occasionType": "birthday",
      "occasionNote": "Bring a small cake",
      "specialNotes": "Window table if available",
      "createdAt": "2026-05-11T04:12:33.123Z"
    }
  ],
  "pagination": {
    "nextCursor": "4c4a04e4-3e3d-4db7-b6f5-0e2e6d5bb7d4",
    "hasMore": true,
    "limit": 50,
    "total": 1
  }
}

bookings post

POST /api/external/v1/bookings

Creates a booking. Idempotency is required and duplicate submissions are guarded by the API and booking engine.

POST/api/external/v1/bookingsBearer token + X-Merchant-Id + Idempotency-Key
PropertyRequirementNotes
customerNameRequired body field2-100 characters.
emailAt least one of email / phone requiredValid email address. Omit if phone is provided.
phoneAt least one of email / phone requiredDigits with optional leading +, 8-15 digits total. Omit if email is provided.
partySizeRequired body fieldInteger >= 1.
localDateRequired body fieldYYYY-MM-DD in the restaurant's local timezone.
localTimeRequired body fieldHH:MM using 15-minute intervals (e.g. 18:00, 18:15, 18:30, 18:45). Must match a slot from the availability endpoint.
notesOptional body fieldFree text special requests.
occasionTypeOptional body fieldbirthday, anniversary, date_night, business, family, celebration, other.
occasionNoteOptional body fieldShort occasion note.

Example request (cURL)

curl -X POST "https://buqq.online/api/external/v1/bookings" \
  -H "Authorization: Bearer buqq_live_..." \
  -H "X-Merchant-Id: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Idempotency-Key: 6b2d3e1e-97ef-4c8e-8d87-0e5d4d9e32ab" \
  -H "Content-Type: application/json" \
  -d '{
    "customerName": "Jane Doe",
    "phone": "+85512345678",
    "email": "jane@example.com",
    "partySize": 4,
    "localDate": "2026-05-18",
    "localTime": "18:00",
    "notes": "Window table if available",
    "occasionType": "birthday",
    "occasionNote": "Bring a small cake"
  }'

Example request (JavaScript)

const res = await fetch(`https://buqq.online/api/external/v1/bookings`, {
  method: "POST",
  headers: {
    "Authorization": "Bearer buqq_live_...",
    "X-Merchant-Id": "550e8400-e29b-41d4-a716-446655440000",
    "Idempotency-Key": "6b2d3e1e-97ef-4c8e-8d87-0e5d4d9e32ab",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    customerName: "Jane Doe",
    phone: "+85512345678",
    email: "jane@example.com",
    partySize: 4,
    localDate: "2026-05-18",
    localTime: "18:00",
    notes: "Window table if available",
  }),
});
const booking = await res.json();

Success response 201

{
  "id": "4c4a04e4-3e3d-4db7-b6f5-0e2e6d5bb7d4",
  "bookingRef": "PW-1001",
  "customerName": "Jane Doe",
  "phone": "+85512345678",
  "email": "jane@example.com",
  "partySize": 4,
  "bookingTimeUtc": "2026-05-18T11:00:00.000Z",
  "localDate": "2026-05-18",
  "localTime": "18:00",
  "status": "pending",
  "source": "pos",
  "occasionType": "birthday",
  "occasionNote": "Bring a small cake",
  "specialNotes": "Window table if available",
  "createdAt": "2026-05-11T04:12:33.123Z"
}

bookings patch

PATCH /api/external/v1/bookings/:bookingIdentifier/status

Updates the reservation status using either the UUID id or the short bookingRef.

PATCH/api/external/v1/bookings/:bookingIdentifier/statusBearer token + X-Merchant-Id
PropertyRequirementNotes
bookingIdentifierPath paramUUID reservation id or short bookingRef.
statusRequired body fieldpending, checked-in, cancelled, or no-show.

Example request (cURL)

curl -X PATCH "https://buqq.online/api/external/v1/bookings/PW-1001/status" \
  -H "Authorization: Bearer buqq_live_..." \
  -H "X-Merchant-Id: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{ "status": "checked-in" }'

Example request (JavaScript)

const res = await fetch(
  `https://buqq.online/api/external/v1/bookings/PW-1001/status`,
  {
    method: "PATCH",
    headers: {
      "Authorization": "Bearer buqq_live_...",
      "X-Merchant-Id": "550e8400-e29b-41d4-a716-446655440000",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ status: "checked-in" }),
  }
);

Success response 200

{
  "id": "4c4a04e4-3e3d-4db7-b6f5-0e2e6d5bb7d4",
  "bookingRef": "PW-1001",
  "customerName": "Jane Doe",
  "phone": "+85512345678",
  "email": "jane@example.com",
  "partySize": 4,
  "bookingTimeUtc": "2026-05-18T11:00:00.000Z",
  "status": "checked-in",
  "source": "pos",
  "occasionType": "birthday",
  "occasionNote": "Bring a small cake",
  "specialNotes": "Window table if available",
  "createdAt": "2026-05-11T04:12:33.123Z"
}

merchant keys

API key management

API keys are managed entirely from your Buqq merchant dashboard — no API calls required.

To generate, rotate, or revoke your API key, visit the Integrations page in your merchant dashboard. One active key is allowed per merchant at any time. Rotating a key immediately revokes the previous one, so update any connected systems before rotating.

Your Merchant ID (needed for the X-Merchant-Id header) is also shown on that page.

rules

Lifecycle rules

These rules should stay stable across docs, UI, and backend enforcement.

One active API key per merchant.

Refreshing a key rotates it: revoke the old key, mint a new one.

Revoked keys cannot authenticate.

Successful API use updates last_used_at.

Plan entitlement and billing status are checked before access is granted.

Booking creation must be idempotent.

At least one of email or phone is required per booking — both are optional when the other is present.

Duplicate guest/time/party-size bookings within 5 minutes are rejected.

The docs page must describe the backend contract exactly, not just the marketing version.

idempotency

Idempotency

Booking creation is idempotent. Two independent guards prevent duplicate reservations.

PropertyRequirementNotes
Idempotency-Key headerRequired on POST /bookingsA UUID you generate per booking attempt. Include it in every create request.
24-hour dedup windowBehaviourReplaying the same Idempotency-Key with the same request body within 24 hours returns the original 201 response without creating a new booking.
IDEMPOTENCY_CONFLICT (409)ErrorThe same Idempotency-Key was reused with a different request body. Generate a new key for a different booking.
DUPLICATE_BOOKING (409)ErrorA separate 5-minute guard rejects any booking with the same email + time + party size, regardless of idempotency key. Protects against two different callers creating logically identical bookings simultaneously.

Recommended pattern

// Generate a stable key per booking attempt (store it if you need to retry)
const idempotencyKey = crypto.randomUUID();

const res = await fetch(`${BASE_URL}/api/external/v1/bookings`, {
  method: "POST",
  headers: {
    "Authorization": "Bearer buqq_live_...",
    "X-Merchant-Id": "YOUR_MERCHANT_ID",
    "Idempotency-Key": idempotencyKey,   // same key on retry
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ /* booking fields */ }),
});

// On network failure: retry with the SAME idempotencyKey
// On success: discard the key — generate a new one for the next booking

changelog

Changelog

API version history. The external API uses date-based versioning via the URL path.

VersionDateNotes
v1.2June 2026GET /availability response now includes fullSlots — time slots that exist on the date but are at capacity for the requested party size. Backwards compatible additive field.
v1.1June 2026email and phone on POST /bookings are now independently optional — at least one must be provided. Previously both were required. Backwards compatible: existing callers sending both fields are unaffected.
v1.0April 2026Initial release. Availability check, bookings list, booking creation with idempotency, booking status update, and API key lifecycle management.