Skip to main content

Referencing a participant's data

Content names a participant's data one way everywhere: in a condition, in a journey's transitions, and in a {{ }} token in text. A path reads broad to narrow — domain, item, which record, field:

sequenceResponses.SLEEP_CHECK.latest.scores.TOTAL
atomResponses.MOOD.previous
extras.QUIT_DATE.latest.daysUntil
signals.steps.today

Items are codes, records are words​

  • An item is named by its code, written bare: SLEEP_CHECK, not @sequences/SLEEP_CHECK. The path already says what each segment is. Stored content holds ids; a read with ?refs=language gives the codes back, and a write takes either.
  • A record is latest, previous or current. latest is the newest saved, previous the one before it, and current what is in hand: the answers of the sequence being filled in, before it is saved. current exists only while a sequence is answered.
  • What is assigned to the person is named in the path too, by code: tags.ENROLLED, groups.WALKERS, relationships.KEY_WORKER, cohorts.SPRING, tested with exists in a condition ("assignment exists") and created or deleted in a When ("assignment created"): { "source": "tags.ENROLLED", "operator": "exists" }. Conditions read these; a text never shows them.
  • Content names only its own tenant's things and the shared library's.

Where each path can be used​

What can be read depends on where it is written. A sequence being answered has this time's answers in hand; a journey doesn't; a title is shown before anything is answered; a text, an email or a push leaves the app, so it names nothing about the person: only the time.

PlaceWhere it is written
AnsweringA sequence's page and atom conditions, and every text of its atoms
JourneyA transition's If, an action's condition, and a chat message a node sends
TitlesSequence, page, surface and journey names and descriptions, start and next labels
Texts, emails, pushesA node's createUserSmsMessage, createUserEmailMessage and createUserPushNotification
PathHoldsAnsweringJourneyTitlesTexts, emails, pushes
sequenceResponses.<sequence>.latest.scores.<score>a number✓✓✓
sequenceResponses.<sequence>.latest.bands.<score>text✓✓✓
sequenceResponses.<sequence>.latest.atomResponses.<atom>an answer✓✓✓
sequenceResponses.<sequence>.latest.savedAta date and time✓✓✓
sequenceResponses.<sequence>.previous.scores.<score>a number✓✓✓
sequenceResponses.<sequence>.previous.bands.<score>text✓✓✓
sequenceResponses.<sequence>.previous.atomResponses.<atom>an answer✓✓✓
sequenceResponses.<sequence>.previous.savedAta date and time✓✓✓
sequenceResponses.current.atomResponses.<atom>an answer✓
sequenceResponses.current.scores.<score>a number✓
sequenceResponses.current.bands.<score>text✓
atomResponses.<atom>.latestan answer✓✓✓
atomResponses.<atom>.previousan answer✓✓✓
atomResponses.<atom>.currentan answer✓
extras.<extra>.latesttext✓✓✓
signals.<type>.<measure>a number✓✓✓
time.inNodea number✓
time.inJourneya number✓
journeys.<journey>.latest.statustext✓✓✓
journeys.<journey>.latest.startedAta date and time✓✓✓
journeys.<journey>.latest.completedAta date and time✓✓✓
journeys.<journey>.latest.archivedAta date and time✓✓✓
journeys.<journey>.latest.pausedAta date and time✓✓✓
sequences.<sequence>.latest.statustext✓✓✓
sequences.<sequence>.latest.answeredyes or no✓✓✓
sequences.<sequence>.latest.assignedAta date and time✓✓✓
sequences.<sequence>.latest.dueAta date and time✓✓✓
sequences.<sequence>.latest.completedAta date and time✓✓✓
sequences.<sequence>.latest.archivedAta date and time✓✓✓
sequences.<sequence>.latest.pausedAta date and time✓✓✓
tags.<tag>assigned✓✓
groups.<group>assigned✓✓
relationships.<relationship>assigned✓✓
cohorts.<cohort>assigned✓✓
sequenceResponses.<sequence>event✓
atomResponses.<atom>event✓
journeys.currentevent✓
user.firstNametext✓✓✓
user.agea number✓✓✓
user.cohorttext✓✓✓
user.cohort.nametext✓✓✓
time.sinceActivea number✓✓✓
time.now.localHoura number✓✓✓✓
time.now.weekdaytext✓✓✓✓
time.now.datea date and time✓✓✓✓
  • A signal's measure is latest (the newest reading of its type, however old), or for steps, sleep and HRV the day comparison: today, median7d, todayMinusMedian7d, todayOverMedian7d. A new participant has no readings, so a signal clause is false for them until their phone has sent some; give any branch on a measure a way through that always fires. Comparisons against a participant's own baseline aren't computed yet and are refused.
  • Every date and time also has .daysAgo and .daysUntil, counted in the participant's local days: extras.QUIT_DATE.latest.daysUntil.
  • time is a domain of its own: time.now is this moment (its localHour, weekday and date, in the participant's time zone), and in a journey time.inNode and time.inJourney are the seconds since the run arrived at its node and since it started.
  • user.age is in whole years; the date of birth itself is never offered.
  • What content, media, embeds and buttons record is read like any answer: a content atom, a picture, a video or an embed is seen once at least half of it has been on screen for a second; a video or audio file, or a YouTube or Vimeo embed, also has playedPercent (the share of it actually played, each second counted once, so skipping ahead doesn't count) and playedSecs (the time spent playing, rewatches included); a button is pressed, and has pressCount. In a When, the same atom is seen or is pressed each time it records something new: atomResponses.WELCOME_VIDEO.latest.playedPercent at least 80.
  • A field inside JSON nobody declared, a JSON extra or a KinesteX workout's summary, is named by its path inside the value, with the type you give it (number, text, yes or no, or a date read through its days): extras.PROFILE.latest.address.city. In the condition editor, step into the value and choose A field inside…. A field the value doesn't hold is absent, so is missing holds for it.

Checked where it is written​

The server checks every path and token by its place when content is saved. A sequence or a journey saves with its problems listed and can't be published until they are gone; an atom, published on save, is refused. GET /api/v1/references?place= (getReferences) lists what can be named in a place, with each item's code and name: it is what the portal's search offers.

A path that passes the check can still find nothing for a participant — a sequence they haven't answered, an extra never set. That is not an error: a clause on it is false, and a token shows its fallback.

Tokens in text​

{{ path }} puts a value into text; {{ path | fallback: "words" }} says what to show when there is nothing there.

Last time you slept {{ atomResponses.SLEEP_HOURS.latest }}.
Hi {{ user.firstName | fallback: "there" }}
  • A value reads as a person reads it. An answer shows the option's label, a number its unit, a date and time in the participant's time zone ("Tue 4 Nov, 23:10"), time.now.weekday the day's name. An answer's .value gives the stored value instead.
  • A single_select whose option values are atom codes quotes the picked atom's answer with .response.
  • Nothing there shows the fallback, or nothing. A token that isn't a path is a problem at save, like a misspelt path.
  • Each value is escaped for the field it lands in: a rich-text label as HTML, a content body as Markdown, everything else as plain text. A participant's words never become markup.
  • The app fills sequence, page, surface and journey titles and every atom text; the server fills a chat message, text, email or push a node sends, when it is sent.