Actions
In the portal: Journeys, then a node in the builder.
What it is
An action is one thing a journey does the moment a participant arrives at a node.
A node holds a list of them. They run in the order you wrote them, once, on arrival. Each one can carry its own condition, so a single node can give everybody a card and raise a coach task for only some of them, without needing a second node.
There are twelve action types. Ten of them do something the participant never sees directly: a tag, a task, a stored value, a message. Only one of them puts an item on the home screen.
What it looks like
An action is an object whose type names the thing to do, and whose other keys are
that thing's settings. The settings sit beside type, not nested under it.
{
"type": "createUserSequence",
"sequenceId": "@sequences/SLEEP_CHECK",
"surfaceId": "@surfaces/TODO",
"persistent": true
}
An optional condition sits beside them too, and it gates this one action only.
{
"type": "createTask",
"taskTypeId": "@taskTypes/FOLLOW_UP",
"condition": { "field": "latestResponse.SLEEP_CHECK.score", "op": "at_least", "value": 10 }
}
When you would use one
A node that welcomes somebody into a course usually needs three things to happen at once: hand them the welcome card, tag them as enrolled so later steps can recognise them, and send a first chat message. That is one node with three actions, not three nodes.
Use a separate node when what happens next differs. Use another action on the same node when it does not.
The twelve actions
Bold parameters are required. Everything else is optional.
Putting something on the home screen
| Action | Parameters |
|---|---|
createUserSequence | sequenceId, surfaceId, autoOpen, schedule, persistent, dismissable, closable, revisable, unscheduledSubmission |
This is the only action that creates a delivery. A delivery is what appears on the participant's home screen. Everything else on this page happens out of sight.
| Parameter | What it does | Default |
|---|---|---|
sequenceId | The content to deliver. Publish it before you point at it: a draft is refused, and the participant sees nothing about it either way. | required |
surfaceId | Which surface of the home screen it lands in. Leave it out and the item is grouped under the journey's own surface. | the sequence's own default surface |
autoOpen | Open it as soon as they next open the app, instead of leaving it on the home screen for them to tap. It still appears on the home screen, so a dismissed or missed one is still reachable. | false |
schedule | How often it comes back. See Schedules and recurrence. | the sequence's own default |
persistent | Whether it stays on the home screen with a tick once submitted, or leaves. | the sequence's default |
dismissable | Whether the participant is offered a way to hide it. | the sequence's default |
closable | Whether they can back out without submitting. false is the blocking consent screen. | the sequence's default, usually true |
revisable | Whether a submitted answer can be changed afterwards. | the sequence's default, usually true |
unscheduledSubmission | Whether an answer can be added outside the schedule. | the sequence's default |
Moving somebody between journeys
| Action | Parameters | Notes |
|---|---|---|
createUserJourney | journeyId | Starts another journey for the same participant. The target must be published, and a draft is refused. A participant already running that journey is left exactly where they are, so unlike a repeat assign made by a person, this never sends anybody back to the start. |
Remembering something about a participant
| Action | Parameters | Notes |
|---|---|---|
createUserTag | tagId | Adds a tag. Adding one they already have does nothing. |
deleteUserTag | tagId | Removes it. |
createUserGroup | groupId | Puts them in a group. |
deleteUserGroup | groupId | Takes them out. |
updateUserExtra | extraId, value | Stores one value against the participant. Write a value that fits the shape that extra was set up to hold. |
Tags, groups and extras are how a journey leaves itself a note. A later node, a later journey, or a card's own condition can all read them back. See Conditions.
Put a real step between setting state and reading it. A tag is written on the device and confirmed on the server, so read it from a later node rather than from a card on the node that set it.
Reaching a person
| Action | Parameters | Where it lands |
|---|---|---|
sendChatMessage | message, atomId, sequenceId | The participant's chat with this journey. |
notify | title, body | A push notification. The tap opens the home screen. |
sendEmail | html, subject, topolJson | Their email address. |
sendSms | body | Their mobile number, once they have given explicit consent to be texted. Without that consent nothing is sent, and the reason is recorded against the send. |
sendSms appears in the action list in the journey builder, beside the other
messaging actions.
Getting a care provider involved
| Action | Parameters | Notes |
|---|---|---|
createTask | taskTypeId, name, description, priority | Raises a task on the shared Tasks list, where every coach in the tenancy sees it. It is not assigned to anyone. In the builder you choose only the task type, and the task takes its name, description, priority and due date from that type. In an import file you can also set name, description and priority on the action. The due date always comes from the type. |
How they run
Two of them run on the phone. The rest run on the server.
createUserSequence is worked out on the participant's own device, which is why a
new card can appear the instant they submit an answer, with no round trip.
createUserJourney runs in both places, so a chain lands whether or not the phone is
online.
The other ten are reported to the server and carried out there. They are fire and forget: the participant is not held up waiting for a task to be raised or an email to go out.
A failing action never breaks the journey. If the tag has been deleted, or the target journey is still a draft, or the participant has no email address on file, the action is recorded as skipped and everything else carries on. The participant still moves. Nothing errors on their screen.
So when you expect a task and none appears, check the action itself rather than the journey.
A notify action that fires inside quiet hours does not arrive later. The node still runs and everything it created is waiting on the home screen in the morning. A chat message is held instead and delivered when the window closes. Where the announcement is the point, gate the node so it runs in waking hours. See Quiet hours.
Every messaging action carries its own id, which is what keeps two of the same type on one node apart, including where the two carry different conditions. The builder sets the id on anything you save. In an import file you write it yourself, so give every action a short id that is unique within its node.
An action gated on the answer that got the participant there never runs. That behaviour belongs to conditions rather than to actions. Read an answer is spent by the first arrow that uses it before you write any conditional action.
Write the four messaging actions verbatim. A {{ }} token works inside the
content of an atom, and the text of notify, sendEmail, sendSms and
sendChatMessage is sent exactly as written, so a token in one of them reaches the
participant as literal braces. Keep participant detail out of those four, because
they leave the app.
Worked settings
Hand somebody a card and open it immediately.
{ "type": "createUserSequence", "sequenceId": "@sequences/WELCOME",
"autoOpen": true, "persistent": false }
It opens in front of the participant. Once submitted, it leaves the home screen entirely.
A consent screen they cannot walk away from.
{ "type": "createUserSequence", "sequenceId": "@sequences/CONSENT",
"autoOpen": true, "closable": false, "persistent": true }
A standing daily check-in that ticks off and comes back tomorrow.
{ "type": "createUserSequence", "sequenceId": "@sequences/SLEEP_DIARY",
"surfaceId": "@surfaces/TODO", "persistent": true,
"schedule": { "type": "weekly", "days": ["MO","TU","WE","TH","FR","SA","SU"],
"start": { "type": "instant" },
"end": { "type": "after", "after": 4, "period": "weeks" } } }
Escalate without putting anything on their screen. Three actions on one node, no delivery at all.
[
{ "type": "createUserTag", "tagId": "@tags/SUPPORTED" },
{ "type": "createTask", "taskTypeId": "@taskTypes/FOLLOW_UP",
"name": "Sleep score in the supported range", "priority": 2 },
{ "type": "sendChatMessage", "message": "I have asked one of our coaches to get in touch this week." }
]
Ask a question inside the chat and branch on the reply.
{ "type": "sendChatMessage", "atomId": "@atoms/HOW_WAS_THIS_WEEK",
"message": "How did this week go?" }
Attaching an atomId or a sequenceId turns the message into something answerable
in place. It creates a real delivery behind the scenes, so the answer is recorded
properly, shows on Progress, and can be branched on with
newAtomResponse.HOW_WAS_THIS_WEEK. It is the most under-used thing in the whole
vocabulary: an answer with no card and no trip to the home screen.
Next
Schedules and recurrence covers schedule, the one
parameter on this page with a grammar of its own. It also covers the waiting periods
that move a participant on without them doing anything.