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 slotsGET /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.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 bookingPOST /bookings
{
"hold_hash": "Xy7kRtQmNpLsWvBzJcFdEaHgUiOeYtAn",
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"phone": "+441234567890",
"job_role": "Registered Nurse",
"nmc_number": "12A3456B",
"pay_option": "credits",
"agency_contact_name": "John Doe",
"agency_contact_email": "john@acme.co.uk",
"agency_email": "invoices@acme.co.uk"
}
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#
{
"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 |
All list endpoints return a consistent envelope:{
"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:{
"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).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.
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.