API reference

Every booking made through your app, website, WhatsApp or booking page follows the same availability rules. The API is on the Business and Pro plans.

Getting started

Create a key in your console under API and widget, then send it as a bearer token. All requests and responses are JSON; times are Lagos time (Y-m-d H:i:s); money is in naira.

curl https://bukam.acrc.com.ng/api/v1/services \ -H "Authorization: Bearer bk_live_xxxxxxxx"

Errors and limits

Errors use HTTP status codes with a body like {"error": {"code": "taken", "message": "Sorry, that time was just taken."}}. Messages are safe to show to customers.

401Missing or revoked key
402Plan does not include the API, or the booking limit is reached
404Not found
409Taken, full or not allowed in the current status
422Missing or invalid fields
429More than 120 requests a minute per key

Creating bookings

Always send service_id, name, phone (the customer's WhatsApp number) and email; notes is optional. Bookings without an email are refused with error code email. Then, by type:

slotstart "2026-11-14 11:30", optional resource_id (else any free one)
rangestart_date, end_date (check-out), optional resource_id
capacitysession_id, seats, tier_id if the date has classes or ticket types, guests (array of names) when required
dispatchpickup, dropoff, optional when, distance_km, resource_id (assign straight away)

By default you collect payment yourself and the booking is confirmed. Send "collect_payment": true to hold it and get a Paystack link for the deposit in payment.url (with return_url to come back to your app).

curl -X POST https://bukam.acrc.com.ng/api/v1/bookings \ -H "Authorization: Bearer bk_live_xxxxxxxx" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: order-1042" \ -d '{"service_id": 30, "session_id": 812, "tier_id": 1630, "seats": 2, "guests": ["Ada Nwosu", "Obi Nwosu"], "name": "Ada Nwosu", "phone": "08031234567", "collect_payment": true}'

Business

GET/businessYour business profile, hours and booking link.

Catalogue

GET/servicesActive services. Add ?all=1 to include switched-off ones. Each has a type: slot, range, capacity or dispatch.
GET/services/{id}One service.
GET/resourcesStaff, rooms, vehicles and other units.

Inventory push

POST/servicesCreate or update a service. Match on id or your own external_id.
POST/resourcesCreate or update staff, vehicles, rooms or aircraft by external_id; optional service_ids.
POST/sessionsCreate or update a flight, trip, event or class date by external_id: service_id or service_external_id, starts_at, ends_at or duration_min, seats, code, origin, destination, venue, status, and tiers [{external_id, name, price, seats}].
POST/sessions/bulkUp to 500 sessions in one call: {"sessions": [...]}. Each result says ok or why not.
DELETE/sessions/{id} or /sessions/ext:{external_id}Close a date to new bookings.

Availability

GET/availability?service_id=12Slot services: open dates for the next 14 days (&days= up to 60).
GET/availability?service_id=12&date=2026-11-14Slot services: free start times that day, with the resources free at each time. Optional &resource_id=.
GET/availability?service_id=20&start_date=2026-12-20&end_date=2026-12-23Range services: free units and the price for those dates.
GET/sessions?service_id=30Capacity services (flights, trips, events, classes): upcoming dates with seats left, fare classes or ticket types.
POST/quotesPrice and deposit for a request before booking. Same body as creating a booking.

Bookings

POST/bookingsCreate a booking. Send Idempotency-Key to retry safely.
GET/bookingsList. Filters: status, service_id, resource_id, session_id, type, customer_phone, from, to, updated_since, limit, offset.
GET/bookings/{ref}One booking.
POST/bookings/{ref}/rescheduleMove it: start (slot), start_date and end_date (range) or when (dispatch).
POST/bookings/{ref}/cancelCancel, with an optional reason shown to the customer.
POST/bookings/{ref}/statusChange status: confirmed, completed, no_show, in_progress, assigned (with resource_id), requested.
POST/bookings/{ref}/paymentsRecord money you collected yourself: amount, reference.
POST/bookings/{ref}/payment-linkGet a Paystack checkout link for what is still owed.

Customers and reviews

GET/customers?phone=0803...Customers, optionally by phone.
GET/reviewsPublished reviews and the average rating.

Webhooks

GET/webhooksYour webhooks and the event names.
POST/webhooksAdd one: url, events (array, or ["*"]). Returns the signing secret.
DELETE/webhooks/{id}Remove one.

Connect your own system

For airlines, bus operators, ride and delivery apps, hotels and anyone whose own system must stay in charge.

  1. Push your inventory with the endpoints under Inventory push, using your own IDs as external_id. Update seats and prices whenever they change.
  2. Confirm every booking. In the console (API and widget) set your confirm URL and switch it on. Before any booking from the page, WhatsApp or the API is saved, we POST it to you, signed like our webhooks. Reply within 8 seconds.
  3. Hear about changes with webhooks, and push driver assignment or status back with POST /bookings/{ref}/status (you can use resource_external_id).
// We send {"event": "booking.confirm_request", "data": {"ref": "BK-7K3QX9", "type": "capacity", "session": {"id": 812, "external_id": "BQ101-20261201"}, "tier": {"external_id": "Y"}, "seats": 2, "guests": ["Ada Nwosu", "Obi Nwosu"], "price_total": 240000, "customer": {"name": "Ada Nwosu", "phone": "2348031234567"}}} // You reply (200) {"ok": true, "reference": "PNR4XK2", "price_total": 240000} // or {"ok": false, "message": "Only 1 seat left in Economy"} // Rides and deliveries can assign straight away {"ok": true, "reference": "TRIP-991", "price_total": 4200, "resource_external_id": "DRV-17"}

Your reference is stored as external_ref on the booking and you can filter with GET /bookings?external_ref=.

Webhook events

We POST JSON to your URL: {"id", "event", "created_at", "data": {"booking": {...}}}. Check the signature: X-Bukam-Signature: sha256=HMAC_SHA256(timestamp + "." + body, secret) with X-Bukam-Timestamp. Reply with any 2xx; failures retry after 1, 5, 30 minutes, 2 and 12 hours.

booking.createdbooking.confirmedbooking.rescheduledbooking.cancelledbooking.completedbooking.status_changedpayment.succeededtip.receivedreview.created
// PHP: verify a webhook $sig = hash_hmac('sha256', $_SERVER['HTTP_X_BUKAM_TIMESTAMP'] . '.' . file_get_contents('php://input'), $secret); if (!hash_equals('sha256=' . $sig, $_SERVER['HTTP_X_BUKAM_SIGNATURE'])) { http_response_code(401); exit; }

Website widget

No API key needed. One line adds a floating Book Now button that opens your booking page in a sheet:

<script src="https://bukam.acrc.com.ng/widget.js" data-business="your-link" async></script>

Options: data-mode="inline" with data-target="#book" to embed the page, data-label, data-color, data-service to preselect. Any element with data-bukam-open (or data-bukam-open="12" for a service) opens it too, and BukamWidget.open() works from your own code.