Calira (formerly Clustermarket) API (0.1.7)
An account in Calira (formerly Clustermarket) represents an organisation, department, or lab. We have a hierarchical structure following these rules:
- An organisation can have many departments and many labs.
- A department can only have labs.
- A lab can exist by itself, without a department or organisation. We refer to these as independent labs.
graph TD
org[Organisation]
org-->lab0[Lab]
dep1[Department]
dep2[Department]
org-->dep1
dep1-->lab1[Lab]
dep1-->lab2[Lab]
org-->dep2
dep2-->lab3[Lab]
il[Independent Lab]
# Get first page (default behavior) GET /equipment?account=123 # Headers: X-Total-Records: 250, X-Current-Page: 1, X-Per-Page: 100 # Get first page with 50 records GET /equipment?account=123&per_page=50 # Headers: X-Total-Records: 250, X-Current-Page: 1, X-Per-Page: 50 # Get second page with 50 records GET /equipment?account=123&per_page=50&page=2 # Headers: X-Total-Records: 250, X-Current-Page: 2, X-Per-Page: 50 # Get third page with 25 records GET /equipment?account=123&per_page=25&page=3 # Headers: X-Total-Records: 250, X-Current-Page: 3, X-Per-Page: 25
# Example: Get all equipment by iterating through pages # Page 1: GET /equipment?account=123&per_page=100&page=1 # Page 2: GET /equipment?account=123&per_page=100&page=2 # Page 3: GET /equipment?account=123&per_page=100&page=3 # Continue until X-Current-Page * X-Per-Page >= X-Total-Records
- Default behavior: When no pagination parameters are provided, the API returns page 1 with up to 100 records
- Large datasets: For datasets with more than 100 records, you must use pagination parameters to retrieve additional pages
- Recommended approach: Use reasonable page sizes (25-100 records) for optimal performance
- For large datasets: Implement proper pagination logic in your application to retrieve all records
- Rate limiting: Be mindful of API rate limits when making multiple paginated requests
OAuth / authorisation — { "error": "<code>", "error_description": "<message>" }
| HTTP | error | When |
|---|---|---|
401 | unauthorised | Missing, invalid, or expired Bearer token |
403 | unapproved_application | OAuth application not approved by Calira |
403 | resource_authorisation_failure | Missing OAuth scope, lab not authorised for the application, or user lacks permission |
404 | resource_not_found | Unknown API path, or record not found where no endpoint-specific handler exists (e.g. unknown booking ID) |
500 | internal_server_error | Unexpected server error — retry with exponential backoff |
429 | Write endpoint rate limit exceeded | HTML error page (see Rate limiting below) |
All write endpoints are throttled per client IP address:
| Endpoint | Method | Default limit |
|---|---|---|
POST /bookings | Create booking | 30 requests / minute |
PATCH /bookings/{id} | Update booking | 30 requests / minute |
POST /bookings/{id}/invitation | Respond to invitation | 30 requests / minute |
POST /bookings/{id}/manage | Approve, decline, or cancel a booking | 30 requests / minute |
POST /bookings/search | Search bookings | 30 requests / minute |
POST /booking/notes/{id} | Add a booking note | 30 requests / minute |
PATCH /booking/notes/{id} | Update a booking note | 30 requests / minute |
POST /uploads | Register direct upload | 30 requests / minute |
POST /equipment | Create equipment | 30 requests / minute |
PATCH /equipment/{id} | Update equipment | 30 requests / minute |
POST /equipment_training/{user_id} | Add equipment training | 30 requests / minute |
DELETE /equipment_training/{user_id} | Remove equipment training | 30 requests / minute |
Limits are enforced independently per endpoint path (each row above has its own 30/minute bucket per IP). Exceeding a limit returns HTTP 429 Too Many Requests with an HTML error page. Retry with exponential backoff.
Equipment resource not found — { "errors": ["<code>"], "error_messages": "<message>" }
| HTTP | errors | Endpoint |
|---|---|---|
404 | account_not_found | POST /equipment — account_id missing or does not exist |
404 | equipment_not_found | PATCH /equipment/{id} — equipment ID in URL does not exist |
404 | not_found | GET /equipment/contact_people — account_id omitted, or account/equipment not found |
Validation — { "success": false, "message": "...", "errors": [{ "attribute": "...", "message": "..." }], "error_messages": ["..."] } on 422
Booking helper endpoints — { "errors": ["<code>"], "error_messages": ["<message>"] }
Used by GET /equipment_available_slots/{equipment_id}, GET /equipment/{id}/booking_add_ons, GET /equipment/{id}/users, GET /users/{id}, and POST /bookings/search. errors holds a stable machine-readable code; error_messages holds the matching human-readable explanation. Each endpoint section lists its codes.
Manage booking — { "error": "<code>", "error_description": "<message>" }, used by POST /bookings/{id}/manage.
Booking notes — { "success": null | false, "message": "...", "booking_id": null, "booking_note": null, "errors": [...], "error_messages": [...] }, used by /booking/notes/{id}.
| HTTP | Condition | Response body |
|---|---|---|
401 | Missing or invalid access token | { "error": "unauthorised", ... } |
403 | Missing write_bookings OAuth scope | { "error": "resource_authorisation_failure", ... } |
403 | OAuth application not approved | { "error": "unapproved_application", ... } |
403 | Application not authorised for the booking's lab | { "error": "resource_authorisation_failure", ... } |
403 | Token user is not the invited user | { "error": "resource_authorisation_failure", ... } |
403 | Token user has no invitation for this booking ID | { "error": "resource_authorisation_failure", ... } |
403 | Accepting when invitee already has an overlapping booking on the same equipment (request_scope: current) | { "error": "resource_authorisation_failure", ... } |
403 | Accepting an invitation that is already accepted (reject is still allowed on accepted invitations) | { "error": "resource_authorisation_failure", ... } |
404 | Booking ID does not exist | { "error": "resource_not_found", ... } |
422 | status omitted, null, or not accept/reject | { "error": "invalid_invitation_status", "error_description": "The status parameter must be either 'accept' or 'reject'." } |
422 | request_scope not current, subsequent, or all | { "errors": [{ "attribute": "base", "message": "unknown request_scope: ..." }] } |
200 | Bulk accept (subsequent/all) blocked by scheduling conflicts and accept_available: false | { "errors": [{ "attribute": "base", "message": "Cannot complete action: the invitee already has overlapping bookings..." }], "conflicting_invitations": [...], "processed_invitations": [] } |
429 | More than 30 requests per minute per IP | HTML error page |
500 | Unexpected server error | { "error": "internal_server_error", ... } |
Does not require lab authorisation — only a valid write_equipment or write_bookings token.
| HTTP | Condition | Response body |
|---|---|---|
401 | Missing or invalid access token | { "error": "unauthorised", ... } |
403 | Token has neither write_equipment nor write_bookings | { "error": "resource_authorisation_failure", ... } |
403 | OAuth application not approved | { "error": "unapproved_application", ... } |
422 | See field table below | { "success": false, "message": "The upload could not be registered...", "errors": [...] } |
429 | More than 30 requests per minute per IP | HTML error page |
500 | Unexpected server error | { "error": "internal_server_error", ... } |
attribute | When |
|---|---|
filename | Missing or blank |
content_type | Missing or blank |
content_type | Not a supported image type when purpose is photo (image/png, image/jpeg, image/jpg, image/svg+xml) |
content_type | Not a supported document type when purpose is document |
content_type | Not a supported booking note file type when purpose is booking_note |
byte_size | Missing, zero, or not a positive integer |
byte_size | Exceeds 10 MB when purpose is photo |
byte_size | Exceeds 25 MB when purpose is document |
byte_size | Exceeds 20 MB when purpose is booking_note |
checksum | Missing or blank (must be base64-encoded MD5) |
purpose | Not photo, document, or booking_note |
| HTTP | Condition | Response body |
|---|---|---|
401 | Missing or invalid access token | { "error": "unauthorised", ... } |
403 | Missing write_equipment OAuth scope | { "error": "resource_authorisation_failure", ... } |
403 | OAuth application not approved | { "error": "unapproved_application", ... } |
403 | Application not authorised for the lab (account_id) | { "error": "resource_authorisation_failure", ... } |
403 | User cannot create equipment in the lab (no equipment_id; e.g. guest role) | { "error": "resource_authorisation_failure", ... } |
403 | User cannot update the equipment (with equipment_id) | { "error": "resource_authorisation_failure", ... } |
404 | account_id query parameter omitted | { "errors": ["not_found"], "error_messages": "No lab, account, or equipment was found..." } |
404 | account_id or equipment_id does not exist | { "errors": ["not_found"], "error_messages": "No lab, account, or equipment was found..." } |
500 | Unexpected server error | { "error": "internal_server_error", ... } |
HTTP-level errors
| HTTP | Condition | Endpoint | Response body |
|---|---|---|---|
401 | Missing or invalid access token | Both | { "error": "unauthorised", ... } |
403 | Missing write_equipment OAuth scope | Both | { "error": "resource_authorisation_failure", ... } |
403 | OAuth application not approved | Both | { "error": "unapproved_application", ... } |
403 | Application not authorised for the lab | Both | { "error": "resource_authorisation_failure", ... } |
403 | User cannot create equipment in the lab | Create | { "error": "resource_authorisation_failure", ... } |
403 | User cannot update the equipment | Update | { "error": "resource_authorisation_failure", ... } |
404 | account_id missing or does not exist | Create | { "errors": ["account_not_found"], ... } |
404 | Equipment ID in URL does not exist | Update | { "errors": ["equipment_not_found"], ... } |
422 | See field tables below | Both | { "success": false, "message": "Equipment could not be created/updated...", "errors": [...] } |
429 | More than 30 requests per minute per IP | Both | HTML error page |
500 | Unexpected server error | Both | { "error": "internal_server_error", ... } |
attribute | When |
|---|---|
account_id | Required on create but missing |
name | Required on create but missing, or empty string |
category | Required on create but missing, or empty string |
base | Update request body is empty ({}) |
availability_status | Not Always Available or Unavailable |
pricing.currency | Not a supported currency code |
pricing.charging_type | Not perHour or perDay |
pricing.price_details | Wrong type (must be object with internal) |
pricing.* | Unsupported nested field (e.g. unknown property under pricing) |
booking_rules.min_booking_time.unit | Not perMinute, perHour, or perDay |
booking_rules.max_booking_time.unit | Not perMinute, perHour, or perDay |
booking_rules.time_step.unit | Not perMinute, perHour, or perDay |
booking_rules.start_step.start_step | Not quarter, halfHour, fullHour, fullDay, or custom |
booking_rules.advance_booking_must.unit | Not hours, days, or weeks |
booking_rules.advance_booking_can.unit | Not hours, days, or weeks |
photos | Wrong type on create (must be array of signed_id strings) |
photos.action | Not extend, replace, destroy, or remove (update only) |
photos.signed_ids | Required but missing or empty for extend/replace/remove (update) |
documents | Same rules as photos |
documents.action | Same rules as photos.action |
documents.signed_ids | Same rules as photos.signed_ids |
| Any field | Wrong JSON type (e.g. string where object expected) |
<section>.<field> | Unsupported property not in the API schema |
422 attachment errors (create and update)
attribute | When |
|---|---|
photos | remove action used on create (not supported) |
documents | remove action used on create (not supported) |
photos | Unsupported action value (not extend, replace, destroy, or remove) |
documents | Unsupported action value |
photos.signed_ids | Required for extend/replace/remove but missing or empty |
documents.signed_ids | Required for extend/replace/remove but missing or empty |
photos.signed_ids | signed_id references an attachment not on this equipment (remove on update) |
documents.signed_ids | signed_id references an attachment not on this equipment |
photos.signed_ids | Unknown or expired signed_id |
documents.signed_ids | Unknown or expired signed_id |
base | signed_id registered by a different user |
base | signed_id registered by a different OAuth application |
base | signed_id registered with wrong purpose (photo vs document) |
base | File bytes not uploaded to direct_upload.url (step B incomplete) |
base | signed_id unknown or expired — register a new upload via POST /uploads |
422 model / business-rule errors (create and update)
attribute | When |
|---|---|
name | Required but blank |
category | Required but blank, or not a valid category |
subcategory | Required for the selected category but missing |
subcategory | Set without category, or invalid for the selected category |
manufacturer | Required but blank, or longer than 200 characters |
model_number | Required but blank, or longer than 200 characters |
availability_status | Invalid value |
contact_access_role_ids | ID is not an admin or instructor for the lab — use GET /equipment/contact_people |
group_ids | Contains invalid group IDs for this lab |
group_ids | Users and equipment already assigned together in another group |
charging_type | Unsupported charging type |
currency | Unsupported currency |
purchase_price | Not between 0 and 99,999,999.99 |
internal_id | Longer than 100 characters |
room | Longer than 255 characters |
serial_number | Longer than 255 characters |
min_booking_time | Duration is zero |
min_booking_time | Duration exceeds maximum booking time |
max_booking_time | Duration is zero |
max_booking_time | Duration exceeds 90 days |
max_booking_time | Invalid configuration |
time_step | Duration is zero |
time_step | Duration exceeds maximum booking time |
start_step | Invalid custom start step |
photos | File exceeds 10 MB or is not an image |
documents | File exceeds 25 MB or is unsupported type |
other_attributes | Invalid add-ons in pricing.custom_addons |
children | category is Parent equipment and the equipment has no children. Message: "Parent equipment is required to have at least one child." |
children | children sent when category is not Parent equipment. Message: "can only be added when category is Parent equipment" |
children | The same child id appears more than once in one update. Message: "The list of child equipment must be unique and cannot contain duplicates." Nothing is updated. |
children.N.inherits_parent_maintenance | The two inherit flags would differ. Message: "must match inherits_parent_properties — maintenance is included in Inherit properties from parent" |
description, lab_rules, serial_number, availability_status, internal_id, contact_access_role_ids, photos, documents | PATCH of the child itself while inherits_parent_properties is true. Attribute is the field that was sent. Message: "cannot be updated while inherits_parent_properties is true — set inherits_parent_properties to false to change this field". name can still be updated. The same fields inside the parent's children array are accepted and the child keeps the parent value. |
maintenance_contract, maintenance_contract_ids | PATCH of the child itself while inherits_parent_maintenance is true. Message: "cannot be updated while inherits_parent_maintenance is true — set inherits_parent_maintenance to false to change maintenance" |
base | Pricing enabled but pricing.price_details.internal missing |
base | pricing.price_details.internal is not a valid number |
base | Invalid advance pricing configuration (overlapping time slots, etc.) |
A missing or invalid token, a missing write_equipment scope, or an unapproved application uses the OAuth error body { "error": "<code>", "error_description": "<message>" }.
Training failures use { "success": false, "message": "...", "user_id": null, "trained_equipment": null, "errors": [{ "attribute": "...", "code": "...", "message": "...", "equipment_ids": [1001] }], "error_messages": ["..."] }. Nothing is written when the request fails. equipment_ids is omitted when the failure is not about particular equipment. A body that cannot be read (trained_equipment missing or empty, or an unsupported field) is 422 and those errors entries have no code.
| HTTP | errors[].code | attribute | When |
|---|---|---|---|
403 | training_feature_disabled | trained_equipment | A lab that owns equipment in the request does not have training enabled. equipment_ids lists that equipment. |
403 | role_not_permitted | base | The caller is not an admin or an instructor. equipment_ids is omitted. |
403 | account_not_admin | trained_equipment | The caller is not an admin of a lab that owns the equipment, and is not an instructor there either. |
403 | equipment_not_authorised | trained_equipment | The caller is an instructor for the lab but is not assigned to the equipment. |
403 | equipment_not_accessible | trained_equipment | The OAuth application is not authorised for the equipment's lab. |
404 | user_not_found | user_id | No user exists with the path user_id. equipment_ids is omitted. |
404 | equipment_not_found | trained_equipment | An equipment_id does not exist. equipment_ids lists the missing IDs. |
422 | user_not_lab_member | user_id | The user is not a member of a lab that owns the equipment. |
422 | record_invalid | trained_equipment | The training row could not be saved. message is that record's validation error. |
HTTP-level errors
| HTTP | Condition | Endpoint | Response body |
|---|---|---|---|
401 | Missing or invalid access token | Both | { "error": "unauthorised", ... } |
403 | Missing write_bookings OAuth scope | Both | { "error": "resource_authorisation_failure", ... } |
403 | OAuth application not approved | Both | { "error": "unapproved_application", ... } |
403 | Application not authorised for the booking's lab | Both | { "error": "resource_authorisation_failure", ... } |
403 | User cannot create bookings on the equipment | Create | { "error": "resource_authorisation_failure", ... } |
403 | User cannot update the booking | Update | { "error": "resource_authorisation_failure", ... } |
403 | Business rules prevent create/update (overlapping bookings, usage limits, advance notice, etc.) | Both | { "success": null, "message": "Forbidden — one or more application business rules...", "booking": null, "errors": ["..."], "error_messages": ["..."] } |
404 | Booking ID does not exist or is not accessible | Update | { "success": null, "message": "No booking exists with that ID...", "booking": null, "errors": ["..."], "error_messages": ["..."] } |
422 | See field tables below | Both | { "success": false, "message": "The booking could not be created/updated...", "booking": null, "errors": [...] } |
429 | More than 30 requests per minute per IP | Both | HTML error page |
500 | Unexpected server error | Both | { "error": "internal_server_error", ... } |
attribute | When |
|---|---|
equipment_id | Required but missing |
start_time | Required but missing or blank |
end_time | Required but missing or blank |
booking_type | Not use or maintenance |
maintenance_provider | Required when booking_type is maintenance but missing |
maintenance_type | Required when booking_type is maintenance but missing |
maintenance_provider | Sent on a use booking (not allowed) |
maintenance_type | Sent on a use booking (not allowed) |
equipment_id | Equipment does not exist |
account_id | Account does not exist |
equipment_id | Equipment is parent equipment (book a child item instead) |
| Any field | Wrong JSON type |
<field> | Unsupported property not in the API schema |
422 schema errors (update)
attribute | When |
|---|---|
base | Empty request body ({}) — at least one field required |
start_time | Blank or wrong type |
end_time | Blank or wrong type |
maintenance_provider | Sent on a use booking (not allowed) |
maintenance_type | Sent on a use booking (not allowed) |
maintenance_provider | Not internal or external (maintenance bookings only) |
maintenance_type | Not a supported maintenance type (maintenance bookings only) |
equipment_id | Not updatable via this endpoint |
account_id | Not updatable via this endpoint |
booking_type | Not updatable via this endpoint |
| Any other field | Unsupported property not in the API schema |
403 business-rule errors (create and update)
Returned when schema validation passes but equipment booking rules reject the request. The errors and error_messages arrays contain human-readable strings (not attribute objects). Common causes include:
- Equipment already booked during the requested time
- Booking would exceed personal or group usage limits
- Booking violates minimum/maximum duration or advance-notice rules on the equipment
- Booking falls outside permitted business hours or usage schedule
- User lacks permission to book the equipment at the requested time
422 time errors (create and update)
attribute | message | When |
|---|---|---|
start_time / end_time | must be an ISO 8601 date-time string with a timezone offset or Z (e.g. 2024-01-15T10:00:00.000Z or 2024-01-15T10:00:00+01:00) | Not an ISO 8601 date-time with Z or an offset |
start_time / end_time | must not include seconds or milliseconds — use a value such as 2024-01-15T10:00:00.000Z | Seconds or milliseconds are not zero |
start_time / end_time | must use 15-minute intervals — only :00, :15, :30, and :45 minutes are supported | Minutes, in the lab's time zone, are not 00, 15, 30, or 45. end_time may also be 23:59 local time. |
Projects, participants, recurrence, add-ons, and booking on behalf of (create and update)
These failures use the booking write body (success, message, booking: null, errors, error_messages). errors and error_messages both contain the message below.
| HTTP | Field | Message (start) | When |
|---|---|---|---|
403 | booking_on_behalf_of | The user associated with your token is not an admin or instructor... | Caller is not an admin or instructor for the equipment |
404 | booking_on_behalf_of | Booking on behalf of user not found or user inactive... | User does not exist, is inactive, or has no access to the equipment |
403 | projects | You are attempting to set a project for a booking in a lab where projects are not enabled... | The lab does not support projects |
404 | projects | Project not found... | project_id does not exist |
403 | projects | Forbidden - The project you provided is a user project not a lab project... | Project is a personal user project |
403 | projects | Forbidden - The project you provided is not valid for the booking owner... | Project belongs to another lab, or the booking owner is not on the project and neither the caller nor the owner manages the lab |
404 | projects | One or more of the project funding codes you provided could not be found... | funding_code_id is not a funding code of that project |
403 | projects | Forbidden - The project funding code percentages ... must add up to 0 or 100... | Sum of every funding_code_percentage is not 0 or 100 |
422 | projects.<n>.project_id | is listed more than once — include each project only once in the projects array | Same project listed twice |
403 | invited_users | Maintenance bookings do not currently support booking participants... | Participants sent on a maintenance booking |
403 | invited_users | The lab setting allowing group bookings is not enabled... | Lab setting Multiple users per booking is off |
403 | invited_users | The user group setting allowing group bookings is not enabled... | Caller's access group does not allow group bookings |
403 | invited_users | Your subscription plan does not support multiple guest bookings. | Lab subscription does not include group bookings |
403 | invited_users | You do not have permission to invite users. | Caller has the guest role in the lab |
403 | invited_users | Participants cannot be added to a booking that has already ended... | Update of a booking whose end time has passed |
403 | invited_users | One or more of the users passed into the invited_users parameter could not be found or is not a member of the lab... | Unknown user, or user not in the equipment's lab |
403 | invited_users | A user without access to book the equipment has been passed into the invited_users parameter... | Participant cannot book the equipment |
422 | recurrence | You have passed an unsupported value to the recurrence_action property... | recurrence_action not skip_conflict, overlap_existing, or decline_existing |
403 | recurrence | Only admins and instructors can create overlapping bookings... | overlap_existing or decline_existing sent by a user who is not an admin or instructor for the equipment |
422 | recurrence | You have passed an unsupported value to recurrence_type property... | recurrence_type not daily, weekly, monthly_relative, monthly_absolute, or yearly |
422 | recurrence | You have passed an unsupported value to recurrence_interval property... | recurrence_interval is not a positive integer |
422 | recurrence | You have passed an unsupported value to recurrence_end property... | recurrence_end is not an ISO 8601 date or date-time |
422 | recurrence | The value of recurrence_end must be after the start_time value | recurrence_end is not after start_time |
422 | recurrence | You have passed an unsupported value to recurrence_days property... | recurrence_days empty or contains a value that is not a lowercase English weekday |
422 | recurrence | You have passed values to recurrence_days when the recurrence type is not weekly... | recurrence_days sent with a type other than weekly |
422 | recurrence | You have passed an unsupported value to recurrence_date property... | recurrence_date not an integer from 1 to 31 |
422 | recurrence | The value of recurrence_date must match the day of the month of start_time... | monthly_absolute with a recurrence_date that differs from the local day of start_time |
422 | recurrence | recurrence_date is only used for monthly_absolute bookings... | recurrence_date sent with monthly_relative |
422 | recurrence | You have passed values to recurrence_date when the recurrence type is not monthly_absolute... | recurrence_date sent with daily, weekly, or yearly |
422 | recurrence | Repeating on the last weekday of the month is not supported... | monthly_relative with a start_time in the fifth week of the month |
403 | add_ons | Maintenance bookings do not currently support equipment add-ons... | Add-ons sent on a maintenance booking |
422 | add_ons | You failed to provide a value for <name> this is a mandatory parameter... | A mandatory add-on is missing (<name> is the add-on), equipment_addon is blank, or quantity is missing for a user_specified add-on |
404 | add_ons | The add-on name you provided could not be found... | equipment_addon is not an add-on the caller can use on this equipment |
422 | add_ons | The add-on quantity you provided is less than 0 or greater than 999999999... | quantity out of range |
422 | add_ons.<n>.equipment_addon | is listed more than once — include each add-on only once in the add_ons array | Same add-on listed twice |
| HTTP | error | When |
|---|---|---|
401 | unauthorised | Missing or invalid access token |
403 | resource_authorisation_failure | Token does not have write_bookings, or the caller's role cannot make this change (for example a researcher approving a booking) |
403 | account_not_authorised | OAuth application or token user is not authorised for the booking's lab |
403 | cancel_not_owner | status: cancelled on a booking the caller neither owns nor participates in |
404 | resource_not_found | Booking ID does not exist |
422 | unsupported_property | Body contains a property other than status and recurrence |
422 | unsupported_status | status missing or not booked, declined, or cancelled |
422 | unsupported_recurrence | recurrence not current_booking, subsequent_booking, or all_bookings |
422 | unsupported_status_change | Change not allowed from the booking's current status (see the endpoint for allowed changes) |
422 | unprocessable_entity | Status change failed a booking rule; error_description explains why |
| HTTP | errors | When |
|---|---|---|
403 | insufficient_scope | Missing read_bookings (GET) or write_bookings (POST, PATCH). This body has only errors and error_messages. |
403 | account_not_authorised | OAuth application or token user is not authorised for the booking's lab |
403 | not_owner_or_participant | GET by a user who is not the booking owner, a participant, or an admin or instructor for the equipment |
403 | not_owner | POST or PATCH by a user who is not the booking owner or an admin or instructor for the equipment |
403 | note_by_another_user | PATCH by the booking owner on a note written by someone else |
404 | booking_not_found | Booking ID does not exist (GET, POST) |
404 | note_not_found | Note ID does not exist (PATCH) |
422 | field errors | See the endpoint for booking_note.note_text and booking_note.note_file_signed_id errors |
