Complete task
const url = 'https://api.aerandil.com/v1/actions/hospitality.action.complete_housekeeping_task/apply';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"branch":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","params":{"completed_at":"2026-04-15T12:00:00Z","consumed":[{"material_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","quantity":"example"}],"expected_holder_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","inspection_outcome":"passed","inspection_rating":1,"notes":"example","resulting_status":"dirty","self_rating":1,"stay_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"},"return_modified":false,"target":{"id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","object_type":"hospitality.housekeeping_task"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.aerandil.com/v1/actions/hospitality.action.complete_housekeeping_task/apply \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "branch": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "params": { "completed_at": "2026-04-15T12:00:00Z", "consumed": [ { "material_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "quantity": "example" } ], "expected_holder_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "inspection_outcome": "passed", "inspection_rating": 1, "notes": "example", "resulting_status": "dirty", "self_rating": 1, "stay_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }, "return_modified": false, "target": { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "object_type": "hospitality.housekeeping_task" } }'Mark a housekeeping task complete and update the room’s status. Without resulting_status, only the unambiguous cascade runs: a departure / vacant_pickup / deep_clean / inspection completed against a dirty or pickup room promotes the cleaning axis to clean, and a stayover / turndown leaves it alone. With resulting_status, the attendant states the room’s cleaning status at Finish (OPERA’s Companion sets it at the same tap) and it is written in the same transaction, through the same refusal the status report applies: a room under an active out-of-order block today cannot have its cleaning status written — return_room_to_service decides. When the most specific active checklist template for the task’s type and room type is required_for_completion, the task refuses to complete until a result for it is recorded (record_checklist_result). On an inspection task, inspection_rating (1–5) and inspection_outcome record the supervisor’s verdict on the inspection and on the cleaning it rated — the most recent completed non-inspection task for the same room and business date, linked by inspection_rates_task (refused when there is none) — and a failed outcome mints a high-priority re-clean of the same type (origin reclean), linked by housekeeping_task_from_inspection. Without resulting_status, a passed inspection leaves the room inspected and a failed one dirty. A cleaning never needs an inspection to count. A runner’s session may not state the room — neither resulting_status nor an inspection’s verdict — and is refused (403): a room’s condition is the word of the attendant who cleaned it and the supervisor who inspected it; a runner’s finish leaves the room to the type-based cascade. A public area’s task (2026-10-14) has no room to state: resulting_status is refused on it, and a public_area_inspection’s verdict rates the area’s latest completed clean of the day and re-mints a failed one for the area, as a room’s inspection does. A minibar or laundry task (2026-10-14) may carry what it consumed — catalogued materials and quantities. A minibar’s are minibar products, consumed from the room’s own minibar store or the house’s default minibar cart and charged at their sale price (the house’s minibar tax rules derived) through post_charge to the bill of the stay that was in the room at completed_at — when the minibar was checked, not when a queued finish lands (2026-10-17) — while it is in house; with nobody in house to charge (the room empty at the check, or its guest gone since) the stock is still consumed, uncharged, and minibar.uncharged.recorded hands the desk its late charge, naming that stay or the last to leave by the check, never a guest who arrived after it; in a shared room stay_id names whose bill (in house, and arrived by the check). A laundry’s are issued from the house’s default laundry store to the room. Any other task type refuses consumed. self_rating (2026-10-15, 1–5) is the attendant’s own rating of the work, stored on the task beside — never instead of — the supervisor’s inspection_rating; it states nothing about the room, so a runner may send it; refused on an inspection. A finish is the holder’s (2026-10-17): an attendant, runner or engineer who does not hold a task a colleague holds is refused (403), while the housekeeping supervisor and the desk finish it on the holder’s behalf; a task nobody holds, or a worker group’s, is taken by the staff member who finishes it, as a start takes it (a non-member is refused, 403). self_rating is the holder’s own word, refused (403) to anyone else — the supervisor, the desk, an agent. expected_holder_id names the holder the caller saw (a finish queued on a handset): refused (422) when someone else, a group or nobody holds the task by the time it lands.
Effects: modify hospitality.housekeeping_task; modify hospitality.room; link hospitality.link.inspection_rates_task; link hospitality.link.housekeeping_task_from_inspection; create hospitality.housekeeping_task; link hospitality.link.housekeeping_task_for_room; link hospitality.link.housekeeping_task_from_triage; link hospitality.link.housekeeping_task_assigned_to; unlink hospitality.link.housekeeping_task_assigned_to_group; link hospitality.link.housekeeping_task_for_area; link hospitality.link.housekeeping_task_requested_by_stay; create hospitality.stock_movement; create hospitality.stock_level; modify hospitality.stock_level; link hospitality.link.stock_movement_for_level; link hospitality.link.stock_level_for_material; link hospitality.link.stock_level_in_store; link hospitality.link.stock_movement_recorded_by; link hospitality.link.stock_movement_for_housekeeping_task; link hospitality.link.stock_movement_for_room; create hospitality.charge; create hospitality.tax_application; link hospitality.link.folio_has_charge; link hospitality.link.charge_has_tax_application; link hospitality.link.charge_for_service_item; link hospitality.link.charge_at_outlet; modify hospitality.folio.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
Apply against an open branch’s overlay view instead of the canonical (main) plane. The action’s writes land in the branch’s copy-on-first-write overlay, its events in the branch’s own stream, and the invocation is journaled for merge-by-replay. Branch writes are action-only, so this is the write-side entry point to a branch (POST /v1/branches forks one).
object
object
When true, the response carries modified_objects and modified_links projections — the engine reads the event log inside the same transaction so the projection observes the handler’s own writes.
object
Responses
Section titled “Responses”Action applied. modified_objects and modified_links populated when return_modified: true was supplied.
Envelope returned by the Aerandil action engine. correlation_id ties every event emitted by this invocation; modified_objects and modified_links project the events the handler emitted (per the action’s declared effects); warnings carries any non-blocking soft-validator observations.
object
Per-invocation correlation id; matches events.correlation_id for every event this action appended.
Links established or removed by this invocation. Empty when the caller did not request return_modified.
object
Objects created, modified, or soft-deleted by this invocation. Empty when the caller did not request return_modified.
object
Non-blocking soft-validator observations emitted during this invocation (e.g. comp-authority over-limit, recipe pour-cost over sell-price). Absent when no warnings were emitted.
object
Human-readable summary, suitable for logging or operator-UI surfacing.
Object type the warning was raised against.
Property the warning anchors to, when a single field is the locus. Absent for multi-field rules.
Stable kebab-case rule identifier (e.g. comp-authority-over-limit).
Example
{ "modified_links": [ { "kind": "linked", "link_type": "hospitality.link.inspection_rates_task" } ], "modified_objects": [ { "kind": "modified", "object_type": "hospitality.housekeeping_task" } ]}Conflict — concurrent modification or unique constraint violation.
Validation error — parameter validation or business-rule violation.