Skip to content

Task Templates

Task templates define the work that gets fanned out to participants in a Task state. Each template specifies what to ask, who to ask, and how to handle responses.

What Task Templates Are

A task template is a reusable definition attached to a Task state in your workflow. When the workflow enters a Task state, the engine resolves the participant list from the template, creates an individual sub-task for each participant, and waits for all sub-tasks to complete before advancing.

Each sub-task is an independent conversation where the participant interacts with the assistant to fulfill the task instructions. The participant’s responses are recorded as gathered data on the parent workflow.

Participant Resolution

The template defines how participants are determined. Three resolution strategies are available:

Static List

A fixed list of participant identifiers (email addresses, Slack user IDs, or contact records). Use this when you know exactly who should participate.

participants:
  - alice@example.com
  - bob@example.com
  - carol@example.com

Gathered Data Reference

Reference a gathered data key from an earlier workflow state. The value should be a list of participant identifiers. Use this when a previous Conversation state collected the participant list from the user.

participants_from: gathered.reviewer_list

Dataset Query

Query a dataset to resolve participants dynamically. Use this when participants are managed in a structured data source – for example, a team_members dataset with name, email, and role columns.

dataset: team_members
filter: role = 'engineer'
participant_field: email

Per-Participant Fan-Out

When the Task state activates, the engine creates one sub-task per resolved participant. Each sub-task:

  • Starts a new conversation between the participant and the assistant
  • Uses the task template’s instructions as the opening message
  • Delivers the message via the participant’s preferred channel (email, Slack, Telegram, or web chat)
  • Runs independently – participants do not see each other’s responses

The assistant interacts with each participant according to the task instructions, collecting their input and recording it with record_data.

Chase Reminders

Task templates support automatic chase reminders for participants who have not responded. Configure:

  • Chase interval – How long to wait before sending a reminder (e.g., 24 hours)
  • Max chases – Maximum number of reminders to send before marking the sub-task as timed out
  • Chase message – Optional custom reminder text

Reminders are delivered via the same channel as the original task message.

Fan-In

The workflow remains in the Task state until all sub-tasks are complete. The all_tasks_complete guard on the outgoing transition evaluates to true only when every participant has responded (or timed out after max chases).

Once fan-in is complete, all participant responses are available as gathered data for subsequent states. The data is keyed by participant identifier, so downstream states can iterate over results or compile them into a summary.

If a participant times out, their sub-task is marked as incomplete but does not block the workflow indefinitely. The all_tasks_complete guard treats timed-out tasks as complete so the workflow can proceed. Check the task_status gathered data to see which participants responded and which timed out.
Technical Details

Sub-task lifecycle – Each sub-task follows the same lifecycle as a regular task run: pending, running, completed, or timed_out. The parent Task state polls sub-task status after each participant interaction and after each chase cycle.

Channel selection – The engine selects the delivery channel for each participant based on their contact record. If the participant has a linked Slack user ID, Slack is preferred. Otherwise, email is used as the fallback. The channel must be enabled on the assistant.

Data aggregation – When a sub-task completes, its gathered data is merged into the parent workflow’s gathered data under a namespaced key: tasks.<participant_id>.<field_key>. This prevents collisions between participants’ responses.