Build Your First Workflow
A workflow is the directed acyclic graph (DAG) of typed nodes that defines what an AI Employee does. This guide walks through building one end to end in the AI Employee builder: defining the inputs it accepts, adding an agent that does the work, wiring the nodes together, passing data between them, testing, and publishing.
By the end you'll have a working single-agent workflow you can run, plus the patterns you need to grow it into something larger.
Prerequisites. You need a builder role (builder, builder admin, system admin, or env admin) to create and edit workflows. If you can't see the workflows or AI Employees area, ask your administrator for access.
What you're building
A workflow always starts from a Start node and is made of:
- Start: declares the workflow's input fields and (optionally) a schedule.
- Agent nodes: the reasoning steps. Each is one of the configurable agent types.
- Transform nodes: optional deterministic reshapes that don't call an LLM.
- Call AI Employee nodes: invoke another AI Employee's workflow as a sub-step, passing inputs and receiving its outputs.
- Send Email nodes: send an email through a connected provider (Outlook or Gmail), with support for draft mode and HITL review before sending.
- Publish nodes: declare the output fields the workflow exposes.
- Schedule nodes: start or stop a recurring run of another dashboard AI Employee, one schedule per tracked item, while this workflow is executing.
- End: terminates a branch.
Nodes are joined by edges. Data flows along edges, and each agent declares an input mapping that pulls specific fields from the Start node or from upstream agents using {{...}} references.
For this guide, build a "Ticket triage" workflow: it accepts a support message, classifies its urgency, and writes a short summary.
Step 1: create the workflow
- Open the AI Employees area and create a new AI Employee (or open the workflows list and create a new workflow). A new workflow opens in the AI Employee builder with a Start node and an End node already placed.
- Give it a name. The name is editable later and is snapshotted onto every run.
The AI Employee builder has an editing surface in the center, an agent library on the left, and a configuration panel that slides in from the right when you select a node.
Step 2: define the inputs on the Start node
The Start node defines the input schema: the typed fields your workflow accepts at run time.
- Select the Start node to open the Configure Start Node panel.
- Under Input Schema, choose Add Field.
- For each field, set:
- Key: the machine name you'll reference later (for example,
message). Keys must be unique; a duplicate key is flagged inline and blocks saving. - Type: one of Text (
string), Number, Integer, Boolean, Array, Object, or File. - Display Title: the human-readable label shown on run forms (for example, "Customer message").
- Required: whether a run must supply the field.
- Key: the machine name you'll reference later (for example,
- Choose Save Schema.
For the triage workflow, add one required Text field with key message and title "Customer message".
The Start node is also where you set a schedule so the workflow runs automatically on a recurring basis. Scheduling requires a published version, because scheduled runs always execute the latest published version. See Core concepts for how versioning works, and Starting an AI Employee Automatically with Triggers for setting up and monitoring a scheduled, webhook, or poll trigger.
The Start node panel also shows a Started by workflows group listing every per-item cron schedule this AI Employee has started on other AI Employees through a schedule node. Each row shows which AI Employee the schedule runs on, its expiry, and a Stop button. A Stop all button stops every schedule this AI Employee started, including any not shown in the list.
Step 3: add an agent
Agents are the reasoning nodes. Ema ships a library of agents built on a small set of base types: including intent classification, extraction, rule validation, and a general-purpose Custom Agent that can search attached Knowledge Bases.
- In the agent library on the left, find the agent you want. For triage, start with Intent classifier (base type
intent_classification) to label urgency. - Add it to the workflow. A new agent node appears with a generated id such as
agent_1. - Select the node to open its configuration panel on the right.
The panel's exact fields depend on the agent type. Common fields include:
- Instructions: what you want the agent to do, in plain language. See Writing effective Instructions.
- Input mapping: which upstream values the agent receives (next step).
- Type-specific configuration: for an intent classifier, the intents (labels) it can return; for an extraction agent, the output schema; for a rule validator, the rules.
- Advanced settings: system prompt, model configuration, execution limits, Knowledge base, Tools, and human-in-the-loop.
Step 4: connect the nodes
Edges define the order of execution and the conditions under which a branch runs.
- Drag from the Start node's output handle to the agent's input handle.
- Drag from the agent to the End node (or to a Publish node; see step 6).
The builder blocks invalid connections as you drag: self-loops, duplicate edges, cycles, edges into the Start node, and edges out of the End or Publish nodes are all rejected, and invalid targets are dimmed.
Conditional edges
To branch on a value, give an edge a condition. Select an edge to open its condition editor. A condition compares a resolved field with an operator and a value. For example, route to an "escalate" branch when output.urgency equals high. Edge conditions reference the source node's output under the output.* namespace.
Conditions come in three shapes:
- Field: compare one resolved field value against an operator. The full operator set includes:
eq,neq,gt,gte,lt,lte,contains,not_contains,substring_of,contains_any_of,contains_none_of,in,not_in,exists,not_exists,array_is_empty,array_is_not_empty,array_contains,array_not_contains,array_contains_any_of, andarray_contains_all_of. - Fixed: a constant boolean; this is the default unconditional edge.
- Group: combine several conditions with
all(AND) orany(OR).
When an agent's branching is driven by an intent classifier, dragging from a per-intent handle automatically stamps the matching output.intent condition onto the new edge.
Step 5: map the agent's inputs
An agent only sees the data you map into it. The input mapping editor (in the agent panel) wires upstream values to the agent's inputs.
- In the agent panel, find Input mapping and choose Add.
- Set the Input name: the key the agent reads (for example,
instructions). - Pick the Source node:
- Workflow inputs: a field from the Start node.
- An upstream agent: a field from that agent's output.
- Pick the Source field. For an upstream agent you can also choose All to pass its whole output.
For triage, map the classifier's instructions input to the Start node's message field.
Some input names are reserved because the agent panel already supplies them. For example, the Instructions field reserves the instructions key. If you try to add a mapping row with a reserved name, it's flagged and won't save. If you set the Instructions field in the panel, you don't need a separate mapping row for it.
Mark a row Optional if the agent should still run when the source value is missing. By default a mapped input is required.
Step 6: declare outputs with a Publish node (optional)
A Publish node declares the fields your workflow exposes as its public output. Dashboard columns and CSV export are derived from publish-node output fields, so add one if you're building a dashboard AI Employee or want a stable output contract.
- Add a Publish node from the agent library.
- Connect your agent to it.
- In the publish-node panel, define the output fields and connect each upstream value to the field it should populate.
A workflow with zero publish nodes simply exposes no output columns. That's a valid choice for chat-style workflows where the response goes straight back to the user.
Splitting an output field into sub-columns
By default, each output field maps to one dashboard column. When an output field's value is a structured object or array of objects, you can split it into one column per property instead.
In the output field editor, turn on Split into columns. New fields have this on by default. When you publish, the backend inspects the wired agent's output schema and stamps the field with the detected sub-column structure. The dashboard then renders one grouped column per property, and the CSV export emits one column per sub-column with the header format Field Name · Property Name.
A few details to keep in mind:
- Split into columns is intent; publish is the gate. If the wired agent's output schema cannot be resolved to an object or array of objects, the field renders as a single column even when the toggle is on.
- Absence is preserved. A field that had the toggle off before this feature was available always renders as a single column, even after republishing.
- CSV headers change. Turning on (or off) the toggle changes the CSV column headers the next time you publish. Account for this in any downstream pipelines that parse the CSV by column position or name.
Step 7: add a Nudge node (optional)
A Nudge node (node type notify_user) sends an outbound card to people outside the current run — for example, to ask someone to approve a decision or collect information. The node supports three operations:
- send — mints a new nudge and delivers cards to the specified recipients.
- update — revises the card on an existing nudge (identified by
nudge_id). - close — settles an existing nudge and records its outcome.
A send can operate in two modes:
- Collect (trigger mode) — the sending step completes at send, and any response starts a separate AI Employee.
- Collect (park mode) — the run pauses until a recipient responds, then continues.
- Announce — sends a card with no expected response; the run continues immediately.
Collect nudge with no expiry warning. If you configure a Collect nudge in trigger mode with no expiry, no reminder cadence, and no close node downstream, the canvas shows a warning: "Nudge has no expiry, no reminder cadence and no close node downstream. It can only end when a person acts. Add an expiry or a close node below it." To resolve it, set an expiry duration on the nudge or place a close node downstream. A reminder cadence alone does not end the nudge — it keeps prompting until the cadence is exhausted, but does not settle the nudge automatically.
Park nudge with no downstream re-read warning. If a park-mode Collect has no node downstream that re-reads data, the canvas shows a warning: the run resumes and decides on values it read before the park. Move any volatile data reads below the nudge node.
Step 8: save, test, and publish
The builder distinguishes three states:
- Save writes the current DAG as a draft version. Every save with a changed DAG creates an immutable draft version (not yet live). The structure is validated on every save: size limits, required fields, unique node ids, valid edge references, and cycle detection. Errors surface immediately.
- Test runs the workflow against your draft so you can confirm behavior before publishing. Use it to iterate quickly.
- Publish validates the workflow again, captures its configuration, and promotes the version to live. Real runs (API, schedule, and channels) execute the active published version.
To test:
- Choose Save to persist your draft (the Publish button is disabled while you have unsaved changes).
- Choose Test and provide values for the input fields you defined. The run streams its progress back to you step by step.
To publish, choose Publish and confirm. Publishing is blocked if validation fails (for example, a missing LLM provider, an invalid human-in-the-loop assignee, or a structurally invalid graph).
Draft vs. live. Saving never changes what's live. Only publishing promotes a version. The builder shows whether your current draft is live (the active version equals the latest) or whether you have unpublished changes (active version is behind the latest).
Builder warnings to act on
The AI Employee builder surfaces issues inline as you edit. Two warnings are worth knowing about before you publish:
Nudge park has no downstream re-read. If you place a notify_user node in park mode (a Collect with no target AI Employee — the run waits for the recipient's response before continuing), the builder shows a warning if nothing downstream of that node re-reads data from an agent, a call_aie node, or a Tools-bearing step. Because no completed node re-executes on resume, a parked run that wakes up after hours or days will make decisions based on values that were resolved before the park. Add at least one agent or call_aie node after the park node to re-read fresh data on resume.
Collect target AI Employee is not published. A notify_user node in trigger mode (a Collect with a target AI Employee set) is refused at publish if that target AI Employee has no published version. The builder names the target and the fix: publish the target AI Employee first, then publish this workflow.
How a run executes
When the workflow runs, the engine:
- Topologically sorts the DAG and runs nodes in dependency order, executing independent branches in parallel.
- Resolves each agent's input mapping by filling in the mapped values from the Start input and upstream outputs.
- Calls each agent, evaluating outgoing edge conditions to decide which branches continue.
- Records a per-step trace: inputs, outputs, status, and token usage. You can inspect this in the run history.
A failed step fails the run (there are no automatic step retries). If an agent pauses for human input, the run pauses and resumes once the person responds. See Designing human-in-the-loop forms.
Reference
| Node type | id prefix | Purpose |
|---|---|---|
start | — | Defines the input schema and optional schedule. One per workflow. |
agent | agent_ | A reasoning step configured from an agent type. |
transform | transform_ | Deterministic, non-LLM field reshaping (no LLM billing, no HITL). |
call_aie | call_aie_ | Invokes another AI Employee's workflow as a sub-step. |
send_email | send_email_ | Sends an email via a connected provider (Outlook or Gmail). |
notify_user | notify_user_ | Sends a nudge card to one or more recipients. The operation field controls whether the node sends a new nudge (send, the default), updates an existing one (update), or settles it (close). |
publish | publish_ | Declares exposed output fields for dashboards and CSV export. |
schedule | schedule_ | Starts or stops a recurring run of another dashboard AI Employee at run time. The operation is either start or stop; schedule_key is bound via input mapping. Carries schedule_config. |
end | — | Terminates a branch. |
| Source | What it maps | Example |
|---|---|---|
| Workflow input | A field from the Start node. | The message field you defined as an input. |
| Upstream agent field | A specific field from an upstream agent's output. | The urgency field from the classifier agent. |
| Upstream agent (all) | The entire output of an upstream agent. | Everything the classifier returned, passed as one object. |
Notify user canvas warning. If you add a notify_user node in trigger-mode Collect (it names a target workflow so the response starts another AI Employee) with no expiry, no reminder cadence, and no close node reachable downstream, the builder shows a warning: "Nudge has no expiry, no reminder cadence and no close node downstream. It can only end when a person acts." This is an advisory, not a publish blocker. Fix it by setting an expires_after_seconds, adding a reminder cadence, or placing a close-operation notify_user node downstream.
Workflows have built-in limits to bound execution: a maximum of 50 nodes and 200 edges, enforced at both save and publish. If you hit them, split the work across multiple AI Employees.
Schedule node
A schedule node starts or stops a recurring run of another dashboard AI Employee, one schedule per tracked item, while this workflow is executing. It is distinct from the workflow-level schedule you set on the Start node: the Start node schedule puts an AI Employee on a cron before any run, whereas a schedule node acts at run time and targets a different AI Employee.
Not yet running. The schedule executor is not yet deployed. A published schedule node fails at run time, and a failed step fails the whole run, so no downstream nodes execute and nothing is started or stopped. Build a schedule node only when you understand this limitation.
Adding a schedule node
-
Create the node. Add a node of type
schedulefrom the agent library. Give it a label such as "Start the digest". Do not try to set schedule configuration here; use the next step. -
Set the schedule config using
workflow_dag_schedule_config_set.For a start operation:
workflow_dag_schedule_config_set( token, node_id="start_digest", operation="start", target_workflow_id="<id of a dashboard AI Employee>", cadence={"amount": 6, "unit": "hours"}, expires_after={"amount": 30, "unit": "days"}, timezone="Europe/London", # optional, omit for UTC )For a stop operation:
workflow_dag_schedule_config_set( token, node_id="stop_digest", operation="stop", selector="self", # or "schedule_key" )Calling
workflow_dag_schedule_config_setagain replaces the entire config, so switchingoperationcannot leave the other operation's fields behind. -
Bind
schedule_keyusingworkflow_dag_input_mapping_edit. Required forstartand forstopwithselector="schedule_key". Skip it only forstopwithselector="self".workflow_dag_input_mapping_edit( token, node_id="start_digest", mode="set", key="schedule_key", source="{{workflow_input.ticket_id}}" )The
schedule_keybinding must not be marked optional. An unresolved optional binding would start a schedule for nothing, and publish refuses it. - Save and publish.
Cadence rules
The cadence {"amount": N, "unit": "..."} must tile its cron field cleanly:
- Minutes: must divide 60 evenly and be less than 60.
- Hours: must divide 24 evenly and be less than 24.
- Days: maximum 28.
- No cadence may fire more often than every 15 minutes.
For example, "every 7 hours" is refused because */7 restarts at the field boundary and would not fire at consistent intervals.
Other constraints
expires_afteris required on a start and has no default. It must not be shorter than the cadence, but it is not subject to the tiling rules (45 minutes is a valid expiry).target_workflow_idmust be the id of a dashboard AI Employee (not a chat AI Employee). Verify it withai_employee_listbefore setting it: publish today checks only that the value is a valid UUID.timezonemust be a valid IANA zone such asEurope/London. The valueLocalis refused.- Fields that belong to
start(target_workflow_id,cadence,timezone,expires_after) are refused on astop, andselectoris refused on astart.
What's next
- Writing effective Instructions — make each agent reliable.
- Setting up a knowledge base — give agents searched context.
- Designing human-in-the-loop forms — pause for human input mid-run.
- Autopilot — build and edit workflows by describing what you want.