Skip to content
Last updated

Flows

A Flow is a conversation graph that belongs to a single Agent. It defines how the agent reacts to user input: which questions to ask, which AI behavior to run, which external API to call, which conditions to branch on.

Flows are agent-scoped. They are not reusable across agents — clone or recreate them if you need a similar shape elsewhere.

Identity

FieldTypeNotes
idnumberUse as flowId in nested routes.
namestringDisplay name.
descriptionstring?
statusenumACTIVE · DRAFT · PAUSED · ARCHIVED.
runCountnumberTotal executions.
assistantIdstringOwning agent (always returned in detail).
isDefaultbooleanOne flow per agent is the default entry point.
liveSnapshotIdstring?MongoDB ObjectId of the published snapshot.
draftSnapshotIdstring?ObjectId of the working draft.
createdAtstringISO timestamp.
updatedAtstringISO timestamp.

The graph

A flow's behavior lives in its graph: a set of nodes connected by edges.

Node types

TypePurpose
STARTEntry point used only by the agent's default Start flow.
TRIGGER_INTENTEntry point for non-default flows; routes by Intent or agentic routing.
SAY_AISends a message. Optionally generated by the AI model.
RESPONSE_AICaptures the user's free-text answer guided by instructions.
TOOLS_AILets the AI decide which custom Tool to call (incl. table tools).
APICalls an external HTTP endpoint and stores the result.
CONDITIONAL_ROUTINGBranches based on AI-evaluated conditions over the context.
CREATE_RECORD_ACTIVITYLogs a manual activity (note, call, meeting, email, WhatsApp) on a specific Object record.

The DYNAMIC_TABLES ("Data Action") node is automation-only and is not available in flows. To read/write records inside a flow, use a TOOLS_AI node with table tools.

Each node has a stable nodeId, an alias, a position for the canvas, and a data payload whose shape depends on type.

nodeId and alias on create: do not send them in POST .../nodes. The server assigns both and returns them in the 201 response. Use that nodeId in edge source / target fields and in downstream {node_<uuid>} references. To rename a node after create, use PUT .../nodes/{nodeId} with { "alias": "..." }.

Edges

Edges connect a node's handle (output port) to another node's targetEdge (input). The same edge object lives both inline on the node and as a global list — the API normalizes both on write.

Reference validation: on create/update, the API validates every cross-resource reference inside a node's data payload — intentIds (TRIGGER_INTENT), customToolIds / playbookIds / selectedRecordTypes[].recordTypeId (TOOLS_AI), assistantId (RESPONSE_AI), aiModelId, knowledgeBaseIds, etc. Each selectedRecordTypes entry must include recordTypeId and at least one permission (read, create, update, delete, or createActivity) set to true. If any referenced id does not exist or does not belong to your account, the request returns 400 bad_request with details.issues[].code === "not_found" and no partial state is persisted. See Errors.

Snapshots

When you publish a flow, the draft snapshot is promoted to the live snapshot. The agent serves traffic from liveSnapshotId. Mutations via POST /nodes, PUT /nodes/{nodeId}, POST /edges, etc. write to the draft.

The Start flow & triggering

  • Default Start flow. Every agent ships with a default flow named "Start" (in DRAFT) containing a single START node. It runs automatically at the beginning of every conversation — first-contact logic (greeting, capturing data, creating a record) belongs here, added after the START node. It's the flow with isDefault: true.
  • The START node is unique. Only the default Start flow may contain a START node; the API rejects a START node in any other flow with 400.
  • Other flows are triggered. A non-default flow begins with a TRIGGER_INTENT node and is entered when it matches the user, configured by either:
    • Intents — intentIds on the TRIGGER_INTENT node; the flow fires when the user's message matches one of them.
    • Agentic routing — agenticRouting, a natural-language instruction describing when to enter the flow; the agent routes based on it and the user's intention.
  • Default-message rule. If a flow finishes a turn without producing a user-facing message (e.g. it only captured variables or ran a Dynamic Tables action), the agent sends a default AI-generated message so the user always receives a reply. This applies to every flow.

Variable interpolation

Text fields can reference variables as {VARIABLE_NAME}. The name must match exactly (case-sensitive). Interpolable fields include:

  • API node: url, headers[].value, parameters[].value, body.
  • Say AI: message, prompt.
  • Response AI: instructions.
  • Tools AI: instructions.
  • Conditional Routing: conditions[].expression.
  • Dynamic Tables: rowId, search, rowData values.
  • Create Record Activity: rowId, content.

See Flow Variables.

Capturing values into variables

Interpolation reads a variable; capturing writes one. The field that populates a variable depends on the node type:

NodeFieldItem shape
Tools AI / Response AI / AI CapturecaptureVariablesreference an existing variable
APIvariables{ key, value, fullResponse } — key names an existing variable, value is a path into the JSON response

Capture targets must reference a variable that already exists. Create it first (POST /agents/{agentId}/variables for flows, POST /workflows/{workflowId}/variables for automations). In captureVariables you may pass just { "name": "<var>" } (or { "id": <id> }) — the API resolves it and stores the canonical { id, name, description } so it is linked and rendered in the app. An unknown name/id (or an API variables[].key that doesn't exist) returns 400.

Captured values are matched by name, so an AI node and an API node can write to the same {name}. The two node families use different fields (captureVariables vs variables); the wrong field on a node is silently ignored.

API node response paths (value)

For an API node, value is a property path into the parsed JSON response — simple property access, not JSONPath (no $, wildcards, or filters):

  • Dot notation for nested objects: data.user.email.
  • [n] for array indices: data.items[0].id ([0] is equivalent to .0; a leading dot is optional).
  • Empty value, or fullResponse: true, captures the whole response body.

For the response {"data":{"items":[{"id":42,"price":9.99}],"total":1}}:

valueCaptured
data.total1
data.items[0].id42
data.items[0].price9.99

Path extraction only applies when the response body is JSON. If a step in the path is missing or null, the variable is left unset (no error). Captured objects/arrays are stored as a JSON string; primitives as their string value.

CREATE_RECORD_ACTIVITY node

Logs a manual activity on a specific Object record during a conversation flow.

{
    "nodeId": "node_44444444-4444-4444-4444-444444444444",
    "type": "CREATE_RECORD_ACTIVITY",
    "position": { "positionX": 640, "positionY": 0 },
    "data": {
        "type": "CREATE_RECORD_ACTIVITY",
        "recordTypeId": 5,
        "rowId": "{contact_row_id}",
        "activityType": "NOTE",
        "content": "El usuario preguntó sobre: {user_question}"
    }
}
FieldRequiredNotes
recordTypeId✅Numeric id of the Object's record type. Validated: must exist in your account.
rowId✅MongoDB ObjectId of the record. Supports {varName} interpolation.
activityType✅NOTE, EMAIL, PHONE_CALL, MEETING, or WHATSAPP. Validated at save time.
content✅Activity body text. Supports {varName} interpolation.

Variables in rowId and content must exist — the API returns 400 if any {varName} is unknown.

Operations

VerbPathPurpose
GET/public/v1/agents/{agentId}/flowsList flows
POST/public/v1/agents/{agentId}/flowsCreate
GET/public/v1/agents/{agentId}/flows/{flowId}Detail (?includeNodes=true for nodes)
PUT/public/v1/agents/{agentId}/flows/{flowId}Update metadata / status
DELETE/public/v1/agents/{agentId}/flows/{flowId}Soft delete
GET/public/v1/agents/{agentId}/flows/{flowId}/graphFull graph (nodes + edges)
POST/public/v1/agents/{agentId}/flows/{flowId}/nodesCreate node
PUT/public/v1/agents/{agentId}/flows/{flowId}/nodes/{nodeId}Update node
DELETE/public/v1/agents/{agentId}/flows/{flowId}/nodes/{nodeId}Delete node + incident edges
POST/public/v1/agents/{agentId}/flows/{flowId}/edgesAdd/replace edge
DELETE/public/v1/agents/{agentId}/flows/{flowId}/edgesRemove edge

CLI

frontline agents flows list --table
frontline agents flows create --name "Order Routing"
frontline agents flows nodes create --data '{"nodeId":"node_11111111-1111-1111-1111-111111111111","type":"TRIGGER_INTENT","position":{"positionX":0,"positionY":0},"data":{"type":"TRIGGER_INTENT","agenticRouting":"Enter this flow when the user asks about an order","intentIds":[]}}'
frontline agents flows nodes create --data '{"nodeId":"node_22222222-2222-2222-2222-222222222222","type":"SAY_AI","position":{"positionX":320,"positionY":0},"data":{"type":"SAY_AI","sayWithAi":false,"message":"I can help route your order request."}}'
frontline agents flows edges add --source node_11111111-1111-1111-1111-111111111111 --source-handle default --target node_22222222-2222-2222-2222-222222222222 --target-handle default