Task Templates

Placeholders

← Back to Task Templates

A placeholder is a token like {{guest.name}} that gets resolved at task-fire time against the live reservation/guest/property/teammate row. They're how a single template can produce a personalised message for every guest.

Syntax

Simple lookup

{{guest.name}}
{{property.address}}
{{reservation.checkInDate}}

The path follows <entity>.<column>. The resolver does a generic SELECT * on the entity table and reads the named column.

Fallback chain

{{a|b|c}}

If a resolves to a truthy value, use a. Otherwise try b, then c. Useful for fields with overrides:

{{reservation.earlyCheckinTime|property.checkInTime}}

Says: use the reservation's earlyCheckinTime override if present; otherwise fall back to the property default.

Nested entity follow

The resolver auto-follows foreign keys. From a task you can write {{reservation.property.name}} or {{reservation.guest.nukiAccessCode}} — the resolver walks the FK chain.

Namespaces

Namespace Common fields Notes
guest name, email, phone, nukiAccessCode, twoNAccessCode, language, country Reads from the canonical guest row for the reservation. After #260 the picker prefers rows with populated access codes.
reservation code, checkIn, checkOut, earlyCheckinTime, lateCheckoutTime, propertyName, totalPrice, nights, guestMood, guestCommunicationType One row per reservation.
property name, address, checkInTime, checkOutTime, wifiPassword, parkingInstructions, guidebookText One row per property.
teammate name, phone, language, noTranslateLanguages The teammate the task is assigned to.
tenant name, defaultLanguage, whatsappPhoneNumberId Tenant-level config.

The full schema is autodiscovered from the DB via SELECT *. If you can put a column in a SELECT against guests, you can write {{guest.<column>}}.

Validation

The editor validates that each placeholder resolves to a real column. After #254, the validator correctly recognises {{guest.email}} and {{guest.name}} (they were flagged as unrecognised before because the namespace list was hardcoded; it now reads from the live schema).

At fire time

When a task fires, the resolver:

  1. Loads the reservation row.
  2. For each placeholder in the action params, walks the path.
  3. Substitutes the value.
  4. If a placeholder doesn't resolve, the runtime decides: - With a fallback chain — try the next. - Without a fallback and required=true — fail the task with a clear error. - Without a fallback and required=false — substitute empty string.

Examples

Hi {{guest.name}},

your access code for {{property.name}} is *{{guest.nukiAccessCode}}*.

You can check in any time after {{reservation.earlyCheckinTime|property.checkInTime}}.
The full address is:
{{property.address}}

WiFi: {{property.wifiPassword}}

resolves to:

Hi Johanna,

your access code for Castle&River Luxe-Vydrica is *847291*.

You can check in any time after 15:00.
The full address is:
Vydrica 12, Bratislava

WiFi: NocV12!

  • #254{{guest.email}} / {{guest.name}} recognised by validator.
  • #185{{property.id}} placeholder schema regression (Add FAQ entry).
  • #260{{guest.nukiAccessCode}} resolves against canonical row.
Source: the FlatsBratislava operator manual.