Skip to content

Your First Workflow

Build Invoice intake with one flow called Check invoice. It accepts an invoice ID and amount. It returns the ID and whether the amount exceeds 1000. Declaring outputs does not supply their values. A Return response node must map them.

The workflow starts in development. This example has no provider call, trigger, or publication. Its rule and samples are teaching examples.

Define what Check invoice receives and returns

Section titled “Define what Check invoice receives and returns”
DirectionDisplay nameTypeOutput name
InputInvoice IDtext—
InputAmountnumber—
OutputInvoice IDtextinvoice_id
OutputReview neededbooleanreview_needed

Keep both inputs Required. For manual Dev tests, supply both. If you omit an input, the run uses its saved test value. Without saved values, it uses "" for text and null for number. Required does not reject that omission.

In the app: open Workflows → New workflow, then rename it Invoice intake using the workflow name. Rename its initial Flow 1 to Check invoice. Under that flow, select Inputs to open Flow inputs, then choose Add new input. Select Outputs to open Flow outputs, then choose Add new output. Expand each row, enter its Display name, and select its type. For inputs, leave Required on. Output labels generate the names above.

With an assistant: complete setup. In CLI, pin the workflow with cai use <workflowId> and omit --workflow afterward. For scoped MCP calls, always pass workflowId. Replace angle-bracket placeholders with returned values.

Repeating create/add commands makes additional objects. When resuming, reuse IDs. Before creating anything again, inspect the workflow list, then the flow list and flow readback.

List workflows and create one to get its ID.

Terminal window
cai workflow list --limit 100 --json
cai workflow create --name "Invoice intake" --json

Before treating the list as complete, check its data.hasMore. Note creation’s data.id as <workflowId>. CLI/MCP creation starts without a flow.

Select the workflow, then list and create its flow for an ID.

Terminal window
cai use <workflowId> --json
cai flow list --json
cai flow create --name "Check invoice" --json

Note data.flowId as <flowId>. Add inputs and outputs, then read back their definitions.

Terminal window
cai flow input add <flowId> --display "Invoice ID" --type text --json
cai flow input add <flowId> --display "Amount" --type number --json
cai flow output add <flowId> --display "Invoice ID" --name invoice_id --type text --json
cai flow output add <flowId> --display "Review needed" --name review_needed --type boolean --json
cai flow get <flowId> --json

Save each data.flowInputId as <invoiceInputId> or <amountInputId> for tests. Each data.jsBinding names the input in expressions.

In the app: choose Add action, search Return response, and add it. In Invoice ID, use Insert dynamic data → Flow Inputs → Invoice ID. In Review needed, select Dynamic data → Flow Inputs → Amount, then the adjacent +, choose >, and set Greater Than → Value to 1000.

Use one Return response here. If several complete, their results merge. In those merged results, overlapping output names overwrite earlier values.

With an assistant: find the action’s slug and component key.

Terminal window
cai integration actions "Return response" --json

Select Return response in data.items. Note its nodeSlug as <slug> and key as <componentKey>.

MCP uses integration_action_list with query: "Return response".

Inspect and add the action to get its node ID.

Terminal window
cai integration action --integration <slug> --key <componentKey> --json
cai node add --flow <flowId> --node <slug> --key <componentKey> --name "Return response" --json

Note data.nodeId as <nodeId>.

MCP uses integration_action_get with integration and key, then node_add with flow for the flow ID and node for the action slug.

Read properties and expression context to check bindings.

Terminal window
cai node props <nodeId> --json
cai expr context <nodeId> --prop invoice_id --json
cai expr context <nodeId> --prop review_needed --json

Confirm the bindings are invoiceID and amount. After renaming an input, reread context.

MCP reads properties with node_prop_list and nodeId.

Set expressions, arrange the flow, and check validation, inputs, outputs, and mappings.

Terminal window
cai expr set <nodeId> --prop invoice_id --js "flow.invoiceID" --json
cai expr set <nodeId> --prop review_needed --js "flow.amount > 1000" --json
cai flow layout <flowId> --json
cai workflow validate --json
cai flow get <flowId> --json
cai node props <nodeId> --json

Proceed when validation reports no errors, flow get shows both inputs and outputs, and node props shows both mappings. The flow readback omits node properties.

MCP writes each expression with node_prop_set, passing nodeId, prop, and inline js.

Validation does not verify that every output is mapped. For an unresolved output, an executed Return response supplies null. Without any completed Return response, the flow result is {}.

These tests return values without storing an invoice. Each end-to-end Dev flow test incurs the base workflow-run charge.

In the app: select Dev, then Test (tooltip: Test current flow). Enter INV-104 / 120 and choose Test flow. In Run history, select Return response → Process and inspect Configurations and Result. Repeat with INV-105 / 1500.

With an assistant: list the accepted inputs to check their IDs.

Terminal window
cai run inputs <flowId> --flow --json

MCP uses run_inputs with nodeIdOrFlowId: "<flowId>" and flow: true.

For either method, match the rows’ IDs to the saved input IDs. Supply every used: true input. Unused inputs are accepted and ignored. CLI and MCP reject unknown keys.

Run both samples for their status and output.

Terminal window
cai run flow <flowId> --target dev --inputs '{"<invoiceInputId>":"INV-104","<amountInputId>":120}' --json
cai run flow <flowId> --target dev --inputs '{"<invoiceInputId>":"INV-105","<amountInputId>":1500}' --json

MCP uses run_flow with flowId, target: "dev", a parsed inputs object, and waitSeconds: 60.

For either method, accept each sample only when data.status is completed and data.output matches the expected result.

Check the first sample for this output.

{"invoice_id":"INV-104","review_needed":false}

Check the second sample for this output.

{"invoice_id":"INV-105","review_needed":true}

If the CLI command does not succeed, it exits non-zero. Note error.detail.workflowExecutionId as <workflowExecutionId>. Before rerunning, read back the execution result.

Terminal window
cai exec get <workflowExecutionId> --json

MCP can return a successful tool call containing a timed-out, paused, or failed execution. Inspect the same ID with execution_get and workflowExecutionId.

Keep both execution results with the workflow link. These samples prove the calculation for two valid invoices. Before accepting arbitrary invoices, define how missing inputs and invalid amounts are handled.

Define flow contracts to extend Check invoice, then Store workflow data for durable records.