Booking checklists (run sheets & gates)
A booking checklist is a checklist staff work through across a booking. It can be:
- Flat โ a single list completed at one moment: pre-departure (before a product line is picked up), pre-check-in (before an experience line is checked in), or a contents check at return.
- Sectioned (run sheet) โ items grouped into named sections spanning the whole booking ("Before arrival", "Client welcome", "Briefing", "Return & closure"). Each section carries one gate anchor:
handoverโ its items must be complete before pickup / check-in.returnโ its items must be complete before return / close-out.nullโ reminder only; never blocks anything.
Which checklists apply to a product or experience is resolved from the supplier's "Applies to" configuration (all products / all experiences / specific products, experiences, or their categories). A line is gated when one or more booking checklists resolve to it. A flat checklist passes its gate when every item is complete with no unresolved critical-item failures; a sectioned run sheet only requires the items anchored to that gate.
A sectioned template produces one checklist instance per line (context: "booking") shared by pickup and return โ progress persists across the whole booking, so staff can start "Before arrival" items days ahead and resume anytime. Flat templates keep separate pickup/return instances as before.
A line can resolve multiple applicable checklists. The booking line therefore returns an array of checks, not a single object.
Booking line pickup_checks / return_checks / checkin_checksโ
Each product line carries pickup_checks and return_checks; each experience line carries checkin_checks. All are arrays of check objects โ empty when the line is not gated for that action. For a template with no checklist instance started yet, the entry is a not_started stub (no checklist_id); the instance is created lazily by the start endpoint below.
A rental-item extra also carries pickup_checks and return_checks when its linked rental product has applicable checklists. Each extra has its own results, even when two extras use the same product. An extra attached to an experience must pass its pickup checks before that session's check-in and its return checks before close-out. A partial rental pickup or return checks only extras attached to the selected rental lines.
{
"pickup_checks": [
{
"required": true,
"status": "in_progress",
"status_label": "In progress",
"has_critical_failures": false,
"progress": { "completed": 9, "total": 21 },
"gate_progress": { "completed": 4, "total": 7, "has_critical_failures": false },
"sections": [
{ "name": "Client welcome", "gate": "handover", "completed": 4, "total": 4, "has_critical_failures": false },
{ "name": "Return & closure", "gate": "return", "completed": 0, "total": 8, "has_critical_failures": false }
],
"checklist_id": 845,
"template_id": 12,
"template_name": "Watercraft rental run sheet",
"items": [ /* WorkshopChecklistItem objects */ ]
}
]
}
| Field | Type | Description |
|---|---|---|
required | boolean | The line is gated for this action. (A sectioned checklist with nothing anchored to the action is omitted from the array.) |
status | string | not_started (no checklist instance yet), or the checklist status: pending, in_progress, completed, completed_with_failures. Reflects the whole checklist. |
status_label | string | Human label for the status. |
has_critical_failures | boolean | true when a critical item anchored to this action has been marked failed. The line is blocked until resolved. |
progress | object | Whole-checklist progress: { "completed": <int>, "total": <int> }. |
gate_progress | object|null | The subset that blocks this array's action (handover for pickup_checks/checkin_checks, return for return_checks): { "completed", "total", "has_critical_failures" }. Equal to progress for flat checklists. |
sections | array | Per-section progress: { name, gate, completed, total, has_critical_failures }. One null-named entry (or empty) for flat checklists. |
checklist_id | integer|null | The checklist instance id. null until the instance is created. |
template_id | integer | The applied checklist template. |
template_name | string | The template name. |
items | array | The checklist items (only populated once an instance exists). Each item carries its section snapshot: section_name, section_sort, section_gate. |
The booking object also carries under_checklist_override (boolean) โ true once the booking has been handed over via an authorized override (see below).
Running a checklist (mobile)โ
POST /api/v1/mobile/bookings/{id}/checklists/start
Body: { "line_type": "product" | "experience" | "extra", "line_id": <int>, "context": "pickup" | "return" } (context defaults to pickup). Ensures the line's checklist instances exist (idempotent) and returns them with item ids. For a sectioned run sheet, pickup and return resolve the same lifecycle instance. Then complete each item:
PATCH /api/v1/mobile/bookings/{id}/checklists/{checklistId}/items/{itemId}
Body: value (string, optional), notes (string, optional), photo (image, optional). The checklist must belong to a line on the booking, which must belong to the requesting supplier.
To untick an item (reset it to incomplete):
DELETE /api/v1/mobile/bookings/{id}/checklists/{checklistId}/items/{itemId}
The gatesโ
A blocked action returns 422 with a distinct error code so clients can tell a gate block apart from a generic failure:
- Pickup (
/actions/pickup,/items/pickup) โcode: "pickup_gate_blocked"โ blocked by incompletehandover-anchored items (or any item of a flat pre-departure checklist). - Check-in (
/actions/check-in,/manifests/bookings/{id}/check-in,/tickets/redeem) โcode: "checkin_gate_blocked"โ same rule for experience lines. - Return (
/actions/return,/items/return) โcode: "return_gate_blocked"โ blocked by incompletereturn-anchored items (or a flat contents check on opted-in products). - Return on a booking that was never picked up (
/actions/return, orPATCHwithstatus: "returned") runs the pickup gate first, because it hands the gear over and back in one go โcode: "pickup_gate_blocked". Anoverrideon that return waives both gates. - Complete (
/actions/complete) โcode: "return_gate_blocked"โ experience close-out:return-anchored sections must be complete before the booking is closed.
Reminder sections (gate: null) never block any action.
error.details carries line_type (product, experience, or extra), line_id, and kind (not_started / in_progress / critical_failure). Product, experience, and extra IDs are independent; use both line_type and line_id to find the blocked line.
Overrideโ
Staff with the bookings.checklist_override permission can proceed through a blocked gate anyway by adding to the action request body:
{ "override": true, "override_reason": "..." }
The override is logged once per booking as a checklist_override booking note and surfaced via under_checklist_override. Requests with override: true from users without the permission return 403.
Status valuesโ
| Status | Meaning |
|---|---|
not_started | The line is gated but no checklist instance exists yet. |
pending | The checklist exists but no items are complete. |
in_progress | Some items are complete; not finished. Sectioned run sheets commonly stay in_progress between pickup and return โ use gate_progress to decide whether a specific action would pass. |
completed | Every item complete with no critical failures. |
completed_with_failures | Complete but a critical item failed (has_critical_failures reflects the relevant gate subset). |
Template sectionsโ
Checklist template resources include a sections array ({ id, name, gate, sort_order }, empty for flat templates), and template items carry section_id (null = ungrouped). Checklist instance items carry the snapshot fields section_name, section_sort, section_gate so the run sheet stays renderable and gateable even if the template later changes.