Deprecated. This API is retiring soon. Switch to the HB Compliance Hub API v2.Switch to API v2
Healthier Business - Virtual BLS
📌
⚠️ This API is in beta. Endpoint shapes, event payloads, and behaviour may change before general availability. Breaking changes will be communicated to registered integrators in advance.
BLS Portal Client API - Overview
The BLS Portal Client API lets agency integrators programmatically manage Basic Life Support training bookings, discover available timeslots, and receive real-time webhook notifications for booking lifecycle events.
Base URL:
https://bls.hbcompliance.co.uk/api/v1How BLS Bookings Work
BLS training is delivered remotely — candidates receive a training kit (mannequin, AED trainer, etc.), complete the course, and return the equipment. This affects two important API behaviours:
Timeslots are dynamic. There are no fixed training events — instead, available date/time slots are generated based on trainer capacity. Query them via
GET /timeslots, hold one via POST /timeslots/hold, then confirm with POST /bookings.Certificates require equipment return.
GET /bookings/{id}/certificate is only available once training_status = completed and equipment_status = in (equipment received back by TDTA). The booking.certificate_available webhook fires at this point.Authentication
All protected endpoints require a Bearer token in the
Authorization header:Obtain a token pair via
POST /auth/token using your hb_client_id and an active API key. Access tokens expire after 1 hour; use POST /auth/refresh (valid 7 days) to rotate without re-entering credentials.Endpoints
Authentication
Method | Path | Auth | Description |
|---|---|---|---|
POST | /auth/token | — | Exchange hb_client_id + api_key for a token pair |
POST | /auth/refresh | — | Rotate access token using a refresh token |
GET | /auth/token-info | ✓ | Inspect the current token |
POST | /auth/revoke | ✓ | Revoke the current token |
Client
Method | Path | Auth | Description |
|---|---|---|---|
GET | /booking-url | ✓ | Your account's unique public booking URL |
GET | /credits | ✓ | Per-course credit balances and GBP money balance |
GET | /transactions | ✓ | Paginated wallet transaction history |
Courses
Method | Path | Auth | Description |
|---|---|---|---|
GET | /courses | ✓ | Active BLS courses — use IDs for timeslot and booking queries |
Timeslots
Method | Path | Auth | Description |
|---|---|---|---|
GET | /timeslots | ✓ | Available date/time slots for a course |
POST | /timeslots/hold | ✓ | Reserve a timeslot for 30 minutes |
DELETE | /timeslots/hold/{holdHash} | ✓ | Release a timeslot hold early |
Bookings
Method | Path | Auth | Description |
|---|---|---|---|
GET | /bookings | ✓ | List bookings (filterable, paginated) |
POST | /bookings | ✓ | Create a booking using a held timeslot |
GET | /bookings/{id} | ✓ | Single booking detail |
GET | /bookings/{id}/certificate | ✓ | Download completion certificate (PDF) |
Webhook Endpoints
Method | Path | Auth | Description |
|---|---|---|---|
GET | /webhooks | ✓ | List registered webhook endpoints |
POST | /webhooks | ✓ | Register a new endpoint (secret shown once) |
GET | /webhooks/{id} | ✓ | Get an endpoint |
PUT | /webhooks/{id} | ✓ | Update name, URL, events, timeout, active state |
DELETE | /webhooks/{id} | ✓ | Delete an endpoint |
POST | /webhooks/{id}/rotate-secret | ✓ | Generate a new signing secret |
POST | /webhooks/{id}/test | ✓ | Send a test delivery |
Timeslot & Booking Flow
The standard booking flow is a three-step process:
Step 1 — Query available slots
Returns a list of
{ date, time, course } objects. Optionally filter to a specific date.Step 2 — Hold a slot
Response includes a
hold_hash and expires_at (30 minutes). Only one active hold is permitted per account at a time — a 409 is returned if one already exists.Step 3 — Create the booking
The hold is released and a booking is created in one step.
Payment Options
pay_option | Behaviour |
|---|---|
credits | Deducts 1 booking credit for the course. Confirmed immediately. |
balance | Deducts the course cost from your GBP money balance. Confirmed immediately. |
invoice | Raised as an invoice to your account. Confirmed immediately. Requires invoice_payments enabled and sufficient credit limit. |
candidate_pays | Candidate receives a Stripe payment link by email. Booking is pending until paid. |
Note: BLS usesinvoice(notinv) for invoice-based payments.
Credits & Balances
GET /credits returns:Booking credits — 1 credit = 1 booking for the specified course.
Booking money balance — a pre-paid GBP balance deducted at the course rate when
pay_option: balance.Certificates
Returns a PDF binary stream. Available only when both conditions are met:
training_status = completed— training was marked complete by TDTA staff.
equipment_status = in— the training kit has been returned to TDTA.
Returns
422 otherwise. Subscribe to booking.certificate_available to know exactly when the certificate is ready.Pagination
All list endpoints return a consistent envelope:
Rate Limiting
60 requests per minute per IP. Exceeding this returns
429 Too Many Requests.Errors
Status | Meaning |
|---|---|
401 | Missing or invalid bearer token |
404 | Resource not found or not owned by your account |
409 | Conflict (e.g. active timeslot hold already exists) |
422 | Validation failed or business rule violation |
429 | Rate limit exceeded |
500 | Server error — contact support |
Webhook Events
The following outbound events are available for subscription:
Event | Fires when |
|---|---|
booking.created | A new booking is confirmed (immediately for credits/balance/invoice; or after Stripe payment for candidate_pays) |
booking.confirmed | A candidate_pays booking transitions from pending to booked after Stripe payment |
booking.rescheduled | A booking is moved to a different timeslot |
booking.cancelled | A booking is cancelled |
booking.attendance_marked | Training attendance is recorded by TDTA staff |
booking.equipment_late | Equipment has not been returned by the expected date |
booking.certificate_available | Training completed and equipment returned — certificate is ready |
webhook.test | Manual test triggered via POST /webhooks/{id}/test |
All events share the same envelope:
Signature Verification
Every webhook request includes these headers:
Header | Description |
|---|---|
X-TDTA-Event | Event type, e.g. booking.created |
X-TDTA-Event-Id | UUID of the event — use for idempotency |
X-TDTA-Delivery-Id | Numeric delivery attempt ID |
X-TDTA-Timestamp | Unix timestamp of dispatch (seconds) |
X-TDTA-Signature | sha256=<hmac> — see below |
Computing the expected signature:
Compare using a constant-time comparison. Reject requests where
|now() - X-TDTA-Timestamp| > 300 (5-minute replay window).PHP:
Python:
Retries
Deliveries are retried up to 5 times with a 15-minute backoff on any non-2xx response or timeout. Return any
2xx status to acknowledge receipt.Contact & Support
| |
|---|---|
Support email | |
Bug reports / feedback | |
Provider | Healthier Business Group |
For urgent issues affecting live bookings, email with the subject line [BLS API URGENT] and include your
hb_client_id and any relevant booking_id.Was this page helpful?
