Skip to content
Last updated

A Workflow is an automation graph that runs at the account level — not tied to any single agent. Workflows fire on triggers (conversation events, schedules, incoming webhooks), execute a graph of nodes, and can call external APIs, transform data, write to Tables, send messages, etc.

If a Flow is "what the agent says next", a Workflow is "what happens around the agent".

Identity

FieldTypeNotes
idnumberUse as workflowId.
namestring
descriptionstring?
statusenumACTIVE · DRAFT · PAUSED · ARCHIVED.
triggerTypeenum?E.g. CONVERSATION_ENDED, CONVERSATION_STARTED, SCHEDULED, etc.
runsCountnumberTotal executions.
runsFailedCountnumberTotal failed executions.
lastRunDatestring?ISO timestamp of the most recent run.
liveSnapshotIdstring?Published snapshot.
draftSnapshotIdstring?Working draft.

The graph

Node types

TypePurpose
TRIGGEREvent-based entry point (conversation ended, contact created, …).
SCHEDULED_TRIGGERCron-style entry point.
WEBHOOKEntry point fed by an Incoming Webhook.
APICall an external HTTP endpoint.
TOOLS_AILet the AI choose a Tool to call.
CONDITIONAL_ROUTINGBranch based on AI-evaluated conditions.
AI_CAPTUREExtract structured data from text using an LLM.
DATA_TRANSFORMERReshape data via prompt.
DYNAMIC_TABLESCreate, update, delete, search, or change the record type of records in a Table or Object.
CREATE_RECORD_ACTIVITYLog a manual activity (note, call, meeting, email, WhatsApp) on a specific Object record.
ITERATIONLoop over an array variable (body / completed / empty handles).
BREAKExit the current loop early and continue on the loop's completed path.
AUTOMATION_STATUSSet another automation live, draft, or toggle its status.
SEND_MESSAGEPush a message to a conversation.
SEND_WHATSAPP_MESSAGESend a WhatsApp Business template to a phone number or People record.
TRANSCRIPTIONTranscribe an audio URL.
FILE_ANALYSISOCR + AI analysis of a file from a URL.

Each node carries nodeId, alias, position, type, and a typed data payload.

Reference validation: on create/update, the API validates every cross-resource reference inside dataaiModelId, customToolIds, tableId, recordTypeId, triggeredByAgentIds[], triggerByWebhookIds[], connectedAccountId, knowledgeBaseIds, etc. 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 the node is not persisted. See Errors.

Schema-level checks also apply to SCHEDULED_TRIGGER (cronExpression validated by cron-validate, timezone against IANA), WEBHOOK and API (url must be a valid URL).

Runtime variables

Automation workflows expose one runtime variable per node, with name = nodeId. A node api_1 can be referenced downstream as {api_1}. This is in addition to explicitly declared Workflow Variables.

Interpolable fields include the same set as Flows, plus:

  • AI Capture: prompt, instructions.
  • Data Transformer: prompt.
  • Tools AI: instructions, prompt.
  • Transcription: audioUrl.
  • Create Record Activity: rowId, content.
  • WhatsApp template variables: templateVariables.header, body, buttons.

Capturing specific values. The API node can extract values from its JSON response into named variables via its variables field ({ key, value, fullResponse }), and AI_CAPTURE / TOOLS_AI / TRANSCRIPTION populate variables via captureVariables. All capture targets must reference a variable that already exists (create it first via POST /workflows/{workflowId}/variables). In captureVariables you may reference it by { "name": "<var>" } or { "id": <id> } — the API resolves and links it to the canonical { id, name, description }; an unknown name/key returns 400. For the API node's value path syntax (dot/[n] property access into the JSON response), see Flows → API node response paths.

CREATE_RECORD_ACTIVITY node

Logs a manual activity on a specific Object record at workflow runtime.

{
    "nodeId": "node_create_activity_1",
    "type": "CREATE_RECORD_ACTIVITY",
    "position": { "positionX": 640, "positionY": 0 },
    "data": {
        "type": "CREATE_RECORD_ACTIVITY",
        "recordTypeId": 5,
        "rowId": "{contact_row_id}",
        "activityType": "PHONE_CALL",
        "content": "Llamada de seguimiento: {call_summary}"
    }
}
FieldRequiredNotes
recordTypeIdNumeric id of the Object's record type. Validated: must exist and belong to your account.
rowIdMongoDB ObjectId of the record to log on. Supports {varName} interpolation.
activityTypeOne of: NOTE, EMAIL, PHONE_CALL, MEETING, WHATSAPP. Validated at save time.
contentActivity body text. Supports {varName} interpolation.

Validation: recordTypeId must point to a real record type on an Object (not a Table) in your account. activityType must be a valid enum value. Variables in rowId and content must exist — the API returns 400 with the offending variable name if any reference is unknown.

ITERATION (Loop) node

Iterates over an array stored in a workflow variable. The wire type is ITERATION.

{
    "nodeId": "node_loop_1",
    "type": "ITERATION",
    "alias": "Loop",
    "position": { "positionX": 320, "positionY": 0 },
    "data": {
        "type": "ITERATION",
        "variableName": "items",
        "variablePath": "data.items",
        "continueOnError": false
    }
}
FieldNotes
variableNameName or alias of an existing workflow variable. Must resolve to an array at runtime.
variablePathOptional dot path inside the variable (e.g. data.items when the variable holds a full API response).
continueOnErrorWhen true, a failed item inside the loop is skipped. When false, any failure stops the automation.

Edges: connect three outgoing handles — body (for-each), completed (after all items), empty (null/empty list; does not route to completed). Inside the loop, {<loopNodeId>.item}, {<loopNodeId>.index}, and {<loopNodeId>.length} are available (use the loop node's nodeId from the graph). {<loopNodeId>.item} is the entire current item (JSON if object) — there is no {<loopNodeId>.item.field} dot-access. DYNAMIC_TABLES SEARCH stores a top-level array of rows; leave variablePath empty when looping that output.

BREAK node

Exits the current loop and continues on the loop's completed path. Place only inside a Loop body branch; outside an active loop the automation fails.

{
    "nodeId": "node_break_1",
    "type": "BREAK",
    "position": { "positionX": 640, "positionY": 0 },
    "data": { "type": "BREAK" }
}

AUTOMATION_STATUS node

Changes another automation's status (SET_ACTIVE, SET_DRAFT, or TOGGLE). Does not publish draft graph changes.

{
    "nodeId": "node_status_1",
    "type": "AUTOMATION_STATUS",
    "position": { "positionX": 320, "positionY": 0 },
    "data": {
        "type": "AUTOMATION_STATUS",
        "automationId": 1,
        "action": "SET_ACTIVE"
    }
}

automationId is validated at save time — it must exist in your account.

SEND_WHATSAPP_MESSAGE node

Sends a WhatsApp Business template message. Distinct from SEND_MESSAGE (conversation push).

{
    "nodeId": "node_wa_1",
    "type": "SEND_WHATSAPP_MESSAGE",
    "position": { "positionX": 320, "positionY": 0 },
    "data": {
        "type": "SEND_WHATSAPP_MESSAGE",
        "recipientMode": "PHONE_NUMBER",
        "personName": "{first_name} {last_name}",
        "phoneNumber": "{phone_number}",
        "phoneNumberId": "1132681309928224",
        "template": "hello_world",
        "templateVariables": { "body": { "1": "{first_name}" } }
    }
}
FieldNotes
recipientModePHONE_NUMBER (default) or PEOPLE_RECORD.
personNameRequired in PHONE_NUMBER mode. Supports {var} interpolation.
phoneNumberRequired in PHONE_NUMBER mode. Full international format. Supports {var}.
peopleRowIdRequired in PEOPLE_RECORD mode. People record ObjectId. Use {var} or {<loopNodeId>.item} when looping scalars — not {<loopNodeId>.item.id}.
phoneNumberIdMeta WhatsApp Business phone number id. Must exist and have an assistantId (validated).
templateApproved template name.
templateVariablesMaps template parameter ids to values (body, header, buttons).

Resolve phoneNumberId and template via the read-only integrations endpoints below.

Integrations (read-only)

VerbPathPurpose
GET/public/v1/integrationsList connected WhatsApp numbers, Instagram accounts, Messenger pages
GET/public/v1/integrations/whatsapp/templatesList APPROVED WhatsApp templates from Meta for this account

For SEND_WHATSAPP_MESSAGE, use a WhatsApp number where canSendMessages is true (an assistant is assigned).

Operations

VerbPathPurpose
GET/public/v1/workflowsList, with status filter
POST/public/v1/workflowsCreate
GET/public/v1/workflows/{workflowId}Detail (?includeNodes=true for nodes)
PUT/public/v1/workflows/{workflowId}Update metadata / status
DELETE/public/v1/workflows/{workflowId}Soft delete + cleanup of triggers
GET/public/v1/workflows/{workflowId}/graphFull graph
POST/public/v1/workflows/{workflowId}/nodesCreate node
PUT/public/v1/workflows/{workflowId}/nodes/{nodeId}Update node
DELETE/public/v1/workflows/{workflowId}/nodes/{nodeId}Delete node + incident edges
POST/public/v1/workflows/{workflowId}/edgesAdd edge
DELETE/public/v1/workflows/{workflowId}/edgesRemove edge
GET/public/v1/workflows/{workflowId}/analyticsRun analytics
GET/public/v1/workflows/{workflowId}/logsList run logs (history)
GET/public/v1/workflows/{workflowId}/logs/{logId}One run + per-node results

Run logs

Every time a workflow runs it records an execution. Read them to audit results, debug failures, or track credit usage.

List /public/v1/workflows/{workflowId}/logs — paginated, with optional status, start_date, and end_date filters. Each run (basic fields):

FieldNotes
idUse as logId.
successfulWhether the run completed without error.
started_atISO timestamp.
completed_atISO timestamp (null while running).
durationMilliseconds.
operationsNumber of node operations executed.
ai_creditsAI credits consumed.
errorError message if the run failed.
insufficient_creditsRun stopped because the account ran out of credits.
prevented_loopRun stopped because a loop was detected.

Detail /public/v1/workflows/{workflowId}/logs/{logId} — the run above plus node_results: [{ node_id, alias, type, success, error, ai_credits, created_at }], one entry per node that executed.

CLI

frontline workflows list --table
frontline workflows create --name "Daily CRM Sync"
frontline workflows nodes create --data '{"nodeId":"trigger_1","type":"TRIGGER","position":{"positionX":0,"positionY":0},"data":{"type":"TRIGGER","triggerType":"CONTACT_CREATED"}}'
frontline workflows analytics --start-date 2026-01-01 --end-date 2026-12-31
frontline workflows logs --workflow-id 2 --table
frontline workflows logs --workflow-id 2 --status FAILED --start-date 2026-01-01
frontline workflows logs get 9001 --workflow-id 2 --pretty
frontline integrations list --table
frontline integrations whatsapp-templates --table