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”| Direction | Display name | Type | Output name |
|---|---|---|---|
| Input | Invoice ID | text | — |
| Input | Amount | number | — |
| Output | Invoice ID | text | invoice_id |
| Output | Review needed | boolean | review_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.
cai workflow list --limit 100 --jsoncai workflow create --name "Invoice intake" --jsonBefore 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.
cai use <workflowId> --jsoncai flow list --jsoncai flow create --name "Check invoice" --jsonNote data.flowId as <flowId>. Add inputs and outputs, then read back their definitions.
cai flow input add <flowId> --display "Invoice ID" --type text --jsoncai flow input add <flowId> --display "Amount" --type number --jsoncai flow output add <flowId> --display "Invoice ID" --name invoice_id --type text --jsoncai flow output add <flowId> --display "Review needed" --name review_needed --type boolean --jsoncai flow get <flowId> --jsonSave each data.flowInputId as <invoiceInputId> or <amountInputId> for tests. Each data.jsBinding names the input in expressions.
Bind the returned values
Section titled “Bind the returned values”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.
cai integration actions "Return response" --jsonSelect 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.
cai integration action --integration <slug> --key <componentKey> --jsoncai node add --flow <flowId> --node <slug> --key <componentKey> --name "Return response" --jsonNote 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.
cai node props <nodeId> --jsoncai expr context <nodeId> --prop invoice_id --jsoncai expr context <nodeId> --prop review_needed --jsonConfirm 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.
cai expr set <nodeId> --prop invoice_id --js "flow.invoiceID" --jsoncai expr set <nodeId> --prop review_needed --js "flow.amount > 1000" --jsoncai flow layout <flowId> --jsoncai workflow validate --jsoncai flow get <flowId> --jsoncai node props <nodeId> --jsonProceed 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 {}.
Test both invoice amounts
Section titled “Test both invoice amounts”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.
cai run inputs <flowId> --flow --jsonMCP 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.
cai run flow <flowId> --target dev --inputs '{"<invoiceInputId>":"INV-104","<amountInputId>":120}' --jsoncai run flow <flowId> --target dev --inputs '{"<invoiceInputId>":"INV-105","<amountInputId>":1500}' --jsonMCP 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.
cai exec get <workflowExecutionId> --jsonMCP 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.
Extend Check invoice
Section titled “Extend Check invoice”Define flow contracts to extend Check invoice, then Store workflow data for durable records.