llms.txt

HB Compliance - Clinical Interviews

📌
⚠️ 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.



Interviews Client API - Overview

The HB Interviews Client API lets agency integrators programmatically manage clinical interview bookings, discover available timeslots, and receive real-time webhook notifications for booking lifecycle events.
Base URL: https://interviews.hbcompliance.co.uk/api/v1



How Interviews Work

All interviews are conducted virtually via Zoom. After a booking is confirmed, the candidate receives a Zoom link by email. This affects two important API behaviours:
Timeslots are dynamic. There are no fixed interview events — available date/time slots are generated based on interviewer capacity. Query them via GET /timeslots, hold one via POST /timeslots/hold, then confirm with POST /bookings.
Interview reports require a completed interview. GET /bookings/{id}/interview-report is only available once interview_status = completed. The booking.interview_report_available webhook fires when the full report has been submitted by the interviewer.



Authentication

All protected endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <access_token>
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-interview-type credit balances and GBP money balance
GET
/transactions
✓
Paginated wallet transaction history

Interview Types

Method
Path
Auth
Description
GET
/interview-types
✓
Active interview types — use IDs for timeslot and booking queries

Timeslots

Method
Path
Auth
Description
GET
/timeslots
✓
Available date/time slots for an interview type
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}/interview-report
✓
Download interview report (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:
1. GET /timeslots?interview_type_id=4&months_ahead=2 → pick a date/time
2. POST /timeslots/hold → hold_hash (valid 30 min)
3. POST /bookings → confirmed booking
Step 1 — Query available slots
GET /timeslots?interview_type_id=4&months_ahead=2&date=2026-07-15
Returns a list of { date, time, interview_type } objects. Optionally filter to a specific date.
Step 2 — Hold a slot
JSON
POST /timeslots/hold
{
"interview_type_id": 4,
"date": "2026-07-15",
"time": "10:00"
}
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
JSON
POST /bookings
{
"hold_hash": "Xy7kRtQmNpLsWvBzJcFdEaHgUiOeYtAn",
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "+441234567890",
"job_role": "Registered Nurse",
"nmc_number": "12A3456B",
"pay_option": "credits",
"agency_contact_name": "John Doe",
"agency_contact_email": "[email protected]",
"agency_email": "[email protected]"
}
The hold is released and a booking is created in one step. The candidate receives a confirmation email with their Zoom interview link.



Payment Options

pay_option
Behaviour
credits
Deducts 1 booking credit for the interview type. Confirmed immediately.
balance
Deducts the interview 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 payment link by email. Booking is pending until paid.



Credits & Balances

GET /credits returns:
JSON
{
"booking_credits": [
{ "course_id": 4, "course_name": "Clinical Interview", "credits": 5 }
],
"booking_money_balance": 480.00,
"currency": "GBP"
}
Booking credits — 1 credit = 1 booking for the specified interview type.
Booking money balance — a pre-paid GBP balance deducted at the interview rate when pay_option: balance.



Interview Reports

GET /bookings/{id}/interview-report
Returns a PDF binary stream. Available only when:
  1. interview_status = completed — the interview has been conducted and marked complete by the interviewer.
Returns 422 otherwise. Subscribe to booking.interview_report_available to know exactly when the report is ready.
Pass ?download=true to force a file download rather than inline rendering.



Booking Statuses

booking_status

Value
Meaning
booked
Interview confirmed
pending
Awaiting candidate payment (candidate_pays flow)
rescheduled
Moved to a new timeslot
cancelled
Cancelled
refunded
Refunded

interview_status

Value
Meaning
scheduled
Interview booked, not yet conducted
in_progress
Interview session is underway
completed
Interview completed by the interviewer
no_show
Candidate did not attend
cancelled
Interview cancelled



Pagination

All list endpoints return a consistent envelope:
JSON
{
"data": [ ... ],
"meta": {
"current_page": 1,
"per_page": 20,
"last_page": 4,
"total": 71
},
"links": {
"next": "https://interviews.hbcompliance.co.uk/api/v1/bookings?page=2",
"prev": null
}
}



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
500
Server error — contact support



Webhook Events

The following outbound events are available for subscription:
Event
Fires when
booking.created
A new booking is created (immediately for credits/balance/invoice; or after payment for candidate_pays)
booking.confirmed
A candidate_pays booking transitions from pending to booked after payment
booking.rescheduled
A booking is moved to a different timeslot
booking.cancelled
A booking is cancelled
booking.interview_completed
The interviewer marks the session as complete (attended or no-show)
booking.interview_report_available
The full interview report has been submitted and is ready to download
webhook.test
Manual test triggered via POST /webhooks/{id}/test
All events share the same envelope:
JSON
{
"event_id": "018f4a2b-1234-7000-8abc-def012345678",
"event_type": "booking.created",
"occurred_at": "2026-06-05T10:00:00+00:00",
"data": { ... }
}

Signature Verification

Every webhook request includes these headers:
Header
Description
X-BLS-Event
Event type, e.g. booking.created
X-BLS-Event-Id
UUID of the event — use for idempotency
X-BLS-Delivery-Id
Numeric delivery attempt ID
X-BLS-Timestamp
Unix timestamp of dispatch (seconds)
X-BLS-Signature
sha256=<hmac> — see below
Computing the expected signature:
signature = HMAC-SHA256(signing_secret, "{timestamp}.{raw_json_body}")
expected = "sha256=" + signature
Compare using a constant-time comparison. Reject requests where |now() - X-BLS-Timestamp| > 300 (5-minute replay window).
PHP:
PHP
function verifySignature(string $body, string $timestamp, string $signature, string $secret): bool {
if (abs(time() - (int)$timestamp) > 300) return false;
$expected = 'sha256=' . hash_hmac('sha256', "{$timestamp}.{$body}", $secret);
return hash_equals($expected, $signature);
}
Python:
Python
import hmac, hashlib, time

def verify(body: bytes, timestamp: str, signature: str, secret: str) -> bool:
if abs(time.time() - int(timestamp)) > 300:
return False
expected = 'sha256=' + hmac.new(
secret.encode(), f'{timestamp}.{body.decode()}'.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)

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 [INTERVIEWS API URGENT] and include your hb_client_id and any relevant booking_id.
Last updated 28 Oct 20267 min readNeed help? Contact support