Skip to content

Variable placeholders — the silent-fail caveat

Variables in automation nodes use {{...}} syntax. Today there’s no validation that a variable path actually exists in the trigger’s data shape — a typo renders as empty in the output, with no error and no warning.

This is the single most common source of “the email went out but the name was blank” debugging sessions.

A variable path is a dotted reference to data from upstream nodes:

{{<triggerNodeId>.tableEntry.data.firstName}}

That reads tableEntry.data.firstName from the trigger node’s output. If the field exists, the value is substituted at run time. If it doesn’t exist, the substitution renders empty — no error.

Who a lead is lives on the contacts attached to it, and the automation editor offers them as their own variable groups — Kontakt Variablen, Teilnehmer Variablen and Gebuchte Personen. They render empty when the request has nobody on it, like any other placeholder.

VariableRenders
$contact.displayNameThe primary contact’s full name
$contact.firstNameFirst name. Falls back to the whole name where it was never split
$contact.middleNameMiddle name
$contact.lastNameLast name
$contact.emailThe contact’s primary email address
$contact.phoneThe contact’s primary phone number
$contact.isSubjecttrue when the lead is about the contact themselves, false when they enquire for somebody else
$subjects.firstNamesFirst names of everyone the lead is about, e.g. “Anni und Billie”
$subjects.namesTheir full names, e.g. “Anni Muster und Billie Muster”
$subjects.countHow many there are
$attendees.firstNamesFirst names of the people booked into this appointment
$attendees.namesTheir full names
$attendees.countHow many are booked
  • $contact is who the message goes to. $subjects is who the lead is about. On a parent’s enquiry for two children those differ, which is what lets one template greet the parent and name the children. On an adult enquiring for themselves, $subjects is that adult.
  • $attendees only fills on the appointment triggers (booked, starts, ends) and names only people whose booking is still live. When two siblings are booked together and one cancels, a reminder names only the one still coming. Use it in confirmation and reminder emails; use $subjects everywhere else.
  • $contact and $subjects are captured when the run is triggered. $attendees is re-read before every step.

Older flows may use $participants.*, $bookedParticipants.* or $contact.isParticipant. They keep working, but the picker no longer offers them — new flows should use the names above.

Pool fields — the course asked about, a note, an enum — are still read the old way, through tableEntry.data.<key> and tableEntry.customFieldData.<uuid>.

The automations list flags flows that still read names, email or phone from lead fields with a “N × alte Kontakt-Variablen” badge. Hover it to see which placeholders are affected, then replace each with the matching $contact or $subjects variable and republish.

Two techniques:

In the node editor, every inserted variable renders as a colored chip. Hover over it: the editor shows the full path and resolves a sample value from the most recent trigger event (or a placeholder if no event has fired yet).

A chip that resolves to grey/blank on hover is a chip whose path doesn’t match any real field. That’s your typo.

For each new flow:

  1. Publish the flow.
  2. Create a real test lead with all the fields you expect populated.
  3. Trigger the flow (e.g. by booking a test appointment).
  4. Inspect the resulting email / SMS / HTTP request payload.
  5. Anything blank that shouldn’t be blank is a path that needs fixing.

This is the only fully reliable verification today.

The most common typo sources:

  • Custom Fields: tableEntry.data.kurs instead of tableEntry.customFieldData.<uuid>. The variable picker inserts the correct path; if you type it by hand, you’ll get it wrong.
  • Identity read from data: tableEntry.data.firstName or an email/phone Custom Field. The path is syntactically valid, but the value is empty or stale — use $contact.firstName, $contact.email, $contact.phone.
  • Renamed pool fields: a flow imported from a template references course, but the pool actually exposes kurs. The path is syntactically valid, semantically wrong — silent fail.
  • Stale references: a Custom Field deletion + recreation gives the new field a different UUID. The flow’s reference still uses the old UUID — silent fail.
  • Trigger-source mismatch: the Customer source picker is set to the wrong pool, so the available paths don’t match the actual lead’s pool.

A future change will introduce plausibility-check warnings:

  • Yellow warning when a node references a path that doesn’t exist in the trigger’s documented output schema.
  • Red error when a node references a path that’s actively wrong (e.g. a deleted custom field UUID).
  • Optional strict mode — refuse to publish a flow with any path warnings.

Until that ships, hover-verify and test-trigger every flow before publishing.

The customFieldData.<uuid> path uses the UUID of the Custom Field, not the field’s name. The UUID is invisible in the editor; the variable picker inserts it correctly. You’ll see something like:

{{<triggerNodeId>.tableEntry.customFieldData.f3a8c2b1-...-...}}

Don’t try to “clean up” that UUID into something readable — the engine looks up the field by UUID at run time. Replacing it with a name silently breaks the variable.

See Custom Field variables for the full path conventions.