Special requests

Special requests

When a guest says "I need parking" or "I'd like wine for arrival" or "Can I bring my dog?" — that's a special request. The platform detects them automatically and routes them through an approval flow before anything guest-visible happens.

How detection works (gh#431)

Inbound guest messages run through an LLM classifier. The classifier is prompt-driven (per gh#431):

  • The system prompt is a per-tenant editable LLM prompt (see AI Cockpit → Prompts, slug special_request_detection).
  • The prompt is the single source of truth for what counts as a special request.
  • No hardcoded category list in code.

When the classifier detects a request, it emits:

  • A category (e.g. wine, parking, beds, early_checkin, late_checkout, pet, other)
  • A normalized intent (e.g. "guest requested wine for arrival")
  • A confidence score

Approval-gated (gh#263)

Detected requests do NOT auto-apply to the reservation. Instead they spawn an approval task:

  • Special request noted: <category> task lands in Operations → Tasks.
  • The task drawer shows the guest message + the classifier's read + buttons: Approve / Reject / Edit.

Earlier (pre-gh#263) the "Early check-in requested" task was notification-only — the operator marked it done thinking they approved, but the reservation stayed earlyCheckin=0 + earlyCheckinTime=null, the cleaner didn't see the time, and the auto-approve rules 60/61 were inactive drafts. That's fixed: marking the task done now actually writes the approval state.

What "Approve" does

For each category, approval writes specific fields:

Category Approve writes
early_checkin reservations.earlyCheckin=1 + earlyCheckinTime=<time>
late_checkout reservations.lateCheckout=1 + lateCheckoutTime=<time>
wine reservations.specialRequest += wine chip
parking reservations.specialRequest += parking chip
beds reservations.specialRequest += beds chip
pet reservations.allowPet=1 + chip
other reservations.specialRequest += free text

These writes propagate to:

  • Calendar chips (🍷 wine / 🛏 beds / 🅿️ parking / eCI? @13:00)
  • Cleaner plán (so the cleaner knows what to prepare / when to finish)
  • Reservation drawer

What "Reject" does

Writes the rejection reason to the task + sends an AI-drafted reply to the guest declining the request (operator reviews before send).

What "Edit" does

Opens the inline editor — change the category, change the time, change the chip text — then approve with the edited values.

Per-tenant configuration

Every aspect is operator-tunable per-tenant:

  • The detection prompt (AI Cockpit → Prompts).
  • The category → field mapping (Task Templates editor).
  • Which categories require approval vs. auto-apply.

A tenant that's OK auto-approving early check-ins under 2h before standard time can flip a per-rule requireApproval=false and skip the human step for that band.

What if detection misses

Some requests slip through (gh#242 — "Special request noted" task earlier fired only 25% of the time; 6 of 8 silently dropped in 14 days because reservations.specialRequest was never populated). The gh#431 prompt-driven rewrite + the gh#242 propagation fix close that gap; if you still see a guest's stated request not surfacing, file a bug report.


Implements: gh#431 (special-request management: prompt-driven, approval-gated, per-tenant), gh#263 (Early check-in requested task is approval, not notification), gh#242 (Special request noted task propagation fix). Related: gh#264 (inline categories on calendar + cleaner plán), gh#264 (eCI? @13:00 time rendering).

Source: the FlatsBratislava operator manual.