
Cargo Orchestration
- 3.4k installs
- 15 repo stars
- Updated August 3, 2026
- getcargohq/cargo-skills
cargo-orchestration is an agent skill that teaches the Cargo CLI to execute actions, trigger batch plays, query orchestration tables, and message AI agents for developers automating revenue workflows programmatically.
About
Drives the Cargo platform at runtime: running actions, workflows, and batches, messaging agents, and querying orchestration tables with SQL. A developer uses it when executing or inspecting Cargo runs and records.
- Run workflows, batches, and agent messages
- SQL queries over runs, batches, spans, and records
Cargo Orchestration by the numbers
- 3,365 all-time installs (skills.sh)
- +537 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #140 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/getcargohq/cargo-skills --skill cargo-orchestrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.4k |
|---|---|
| repo stars | ★ 15 |
| Last updated | August 3, 2026 |
| Repository | getcargohq/cargo-skills ↗ |
How do you trigger Cargo workflows from the CLI?
Execute actions, run workflows, trigger batches, message AI agents, and query runtime tables with SQL via the Cargo CLI.
Who is it for?
RevOps or platform engineers automating Cargo plays, tool workflows, and agent chats via cargo-ai CLI from coding agents or scripts.
Skip if: Teams without a Cargo workspace who only need generic CRM SDK calls outside Cargo's orchestration runtime.
When should I use this skill?
A developer asks to trigger a Cargo play, run a batch on a segment, execute a Cargo tool workflow, or query Cargo orchestration runs with SQL.
What you get
Completed Cargo runs or batches with run UUIDs, execution metrics, and optional SQL query results from orchestration tables.
- completed run or batch results
- orchestration SQL query output
- segment fetch records
By the numbers
- cargo-orchestration skill version 1.5.0
- cargo-skills repo ships 12 skills across 7 CLI domains
- Covers 120+ connector integrations in the Cargo platform
Files
Cargo CLI — Orchestration
Runtime operations for the Cargo platform.
What do you want to run?
Need to run something?
├── One action, one record → action execute
├── One action, many records → action execute-batch
├── Multiple actions chained
│ ├── One-off / ad-hoc → run create --nodes (one record)
│ │ batch create --nodes (many records)
│ └── Reusable workflow → build a tool, then run create --workflow-uuid
│ or batch create --workflow-uuid
└── Conversational AI agent → message createTerminology: An orchestration tool is a saved on-demand workflow (listed viatool list). An action is a single operation you execute without building a workflow — it can embed a saved orchestration tool (kind: "tool"), call a third-party connector (kind: "connector"), invoke an AI agent (kind: "agent"), or run a built-in platform operation (kind: "native").
Composing a node graph? Prefer built-in actions + expressions. Use the
actions Cargo already provides plus template expressions; avoid python,script (JS), and raw HTTP nodes unless you truly have no alternative. Reshapedata →variables; call an LLM and get parsed JSON → nativeagentnode; call an
API → the integration's dedicated connector action; route →branch/filter/switch.
See `references/node-selection.md`.
References:
references/examples/actions.md — action execute and execute-batch examplesreferences/examples/tools.md — tool (on-demand workflow) examplesreferences/examples/plays.md — play (segment-driven automation) examplesreferences/examples/agents.md — AI agent chat examplesreferences/examples/templates.md — pre-built workflow templatesreferences/examples/queries.md—orchestration query execute(ClickHouse: runs/batches/spans/records) SQL examples. Forstorage query(workspace storage), see thecargo-storageskill.
references/examples/segments.md — segment fetch and filter examplesreferences/nodes.md — full node creation guide (kinds, native actions, expressions, validation, routing)references/node-selection.md— how to pick the right node and avoid unnecessary `python` nodes (decision table, native LLMagentnode, template-expression limits, the silent-undefined footgun, inspecting node data viarunContext, Pyodide sandbox limits, what survives adelay, group result access)
references/filter-syntax.md — complete filter condition referencereferences/polling.md — async polling patterns, error handling, retry strategiesreferences/response-shapes.md — full JSON response structuresreferences/troubleshooting.md — common errors, plus a "Debugging a workflow run" section for runs that succeed but produce wrong output (wrong-branch routing, empty downstream values)Prerequisites
See `../cargo/references/prerequisites.md` for install, login (--oauth / --token), JSON output conventions, and error shapes. Verify the session with cargo-ai whoami before running any of the commands below.
Discover resources first
Most commands require UUIDs. Always discover them before acting.
cargo-ai orchestration play list # all plays (name, workflowUuid, modelUuid, segmentUuid)
cargo-ai orchestration tool list # all tools (name, workflowUuid, description)
cargo-ai orchestration workflow list # all workflows (uuid only — no name)
cargo-ai orchestration template list # all workflow templates (slug, name, kind)
cargo-ai ai agent list # all agents (uuid, name)
cargo-ai ai template list # all AI agent templates (slug, name, languageModelSlug)
cargo-ai storage model list # all models (uuid, name, slug, columns)
cargo-ai storage dataset list # all datasets
cargo-ai segmentation segment list # all segments (uuid, name, modelUuid)
cargo-ai connection connector list # all connectorsPlays vs tools: Both are backed by a workflow. A play is a segment-driven automation — it reacts to data changes in a segment (records added, updated, removed). A tool is an on-demand workflow — triggered manually, via API, or on a cron schedule. Workflows don't have a name field; use play list or tool list to find names and extract the workflowUuid.
Retrieve in the UI: plays live at app.getcargo.io/workspaces/<WORKSPACE_UUID>/plays/<PLAY_UUID> and tools at app.getcargo.io/workspaces/<WORKSPACE_UUID>/tools/<TOOL_UUID>. Get <WORKSPACE_UUID> from cargo-ai whoami under workspace.uuid.
Designing a new tool or play? Check templates first — they are pre-built node graphs for common automation patterns (enrichment pipelines, CRM syncs, lead scoring) and are an excellent starting point. List templates with cargo-ai orchestration template list and inspect a specific one with cargo-ai orchestration template get <slug>. Templates are tagged by kind so you can find ones suited for tools ("kind":"tool") or plays ("kind":"play") right away. See references/examples/templates.md for the full guide.
Compatibility rules:
- `run create` — only works with tool workflows (or no
workflowUuid). Play workflows returnplayNotCompatible. - `batch create` — allowed data kinds depend on the workflow type:
- Play workflows:
segment,change,filter,recordIds - Tool workflows (or no
workflowUuid):file,records
Quick reference
# Single actions
cargo-ai orchestration action execute --action '{"kind":"tool","toolUuid":"<uuid>","config":{}}' --data '{"domain":"acme.com"}'
cargo-ai orchestration action execute-batch --action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' --records '[{...},{...}]'
# Workflows (chain multiple actions)
cargo-ai orchestration run create --workflow-uuid <uuid> --data '{"company":"Acme","domain":"acme.com"}'
cargo-ai orchestration run create --data '{"domain":"acme.com"}' --nodes '[...]'
cargo-ai orchestration batch create --workflow-uuid <uuid> --data '{"kind":"segment","segmentUuid":"..."}'
# AI agents
cargo-ai ai message create --chat-uuid <uuid> --parts '[{"type":"text","text":"..."}]'
# Data
cargo-ai orchestration query execute "SELECT count() FROM runs WHERE status='error'" # ClickHouse: spans, runs, batches, records
cargo-ai segmentation segment fetch --model-uuid <uuid> --filter '{"conjonction":"and","groups":[]}' --fetching-limit 100
# For SQL against workspace storage (Companies, Contacts, …), see the cargo-storage skill: `storage query execute`Polling async operations
All operations are asynchronous. Either poll until terminal state, or pass --wait-until-finished to block.
action execute returns a run. action execute-batch returns a batch. They poll the same way:
| Result type | Poll command | Interval | Done when |
|---|---|---|---|
| Run | run get <uuid> | 2s | status is success, error, or cancelled |
| Batch | batch get <uuid> | 5s | status is success, error, or cancelled |
| Agent message | message get <uuid> | 2s | status is success or error |
For long-running batches (1000+ records), increase the interval to 10-15s after the first minute.
Execute actions
Run a single action — no workflow or node graph needed.
# One action, one record → returns a run
cargo-ai orchestration action execute \
--action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' \
--data '{"domain":"acme.com"}' \
--wait-until-finished
# One action, many records → returns a batch
cargo-ai orchestration action execute-batch \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--records '[{"domain":"acme.com"},{"domain":"globex.com"}]' \
--wait-until-finishedAction kinds: tool, connector, agent, native. See references/examples/actions.md for all action kinds, parameters, retry config, response shapes, and end-to-end examples.
Create a run
A run processes a single record through a workflow. Use run create when you need to chain multiple actions together via a node graph, or when running an existing tool workflow.
Runs only work with tool workflows. Play workflows return playNotCompatible — use batch create instead.
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme","domain":"acme.com"}'
# → Poll with: cargo-ai orchestration run get <run-uuid>
# Or wait synchronously — blocks until the run reaches a terminal state and returns the final result
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme","domain":"acme.com"}' \
--wait-until-finishedAlso supports --release-uuid to pin a specific release.
Cancelling runs:
cargo-ai orchestration run cancel --workflow-uuid <uuid> --uuids run-uuid-1,run-uuid-2See references/examples/tools.md for file uploads, monitoring, and cancellation. See references/nodes.md for custom node graphs.
Create a batch
Batches process multiple records at once. Allowed data kinds depend on the workflow type:
- Play workflows:
segment,change,filter,recordIds - Tool workflows (or no
workflowUuid):file,records
# Play workflow — run on a segment
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"..."}'
# Tool workflow — run on a file
cargo-ai orchestration batch create \
--workflow-uuid <tool.workflowUuid> \
--data '{"kind":"file","s3Filename":"..."}'
# → Poll with: cargo-ai orchestration batch get <batch-uuid>
# Or wait synchronously — blocks until the batch reaches a terminal state and returns the final result
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"..."}' \
--wait-until-finishedDownloading results: get the releaseUuid from batch get, then cargo-ai orchestration release get <release-uuid> to find nodes[].slug, then cargo-ai orchestration batch download --uuid <batch-uuid> --output-node-slug <slug>.
Cancelling a batch:
cargo-ai orchestration batch cancel <batch-uuid>See references/examples/plays.md and references/examples/tools.md for filtering, record IDs, file uploads, monitoring, and cancellation.
Send a message to an AI agent
cargo-ai ai agent list # 1. Find the agent
cargo-ai ai chat create \ # 2. Create a chat
--trigger '{"type":"draft"}' \
--agent-uuid <agent-uuid> --name "Research session"
cargo-ai ai message create \ # 3. Send a message
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Find the VP of Sales at Acme Corp"}]'
# → Extract assistantMessage.uuid, poll with: cargo-ai ai message get <uuid>
# Done when .message.status is "success" (read .parts) or "error" (read .errorMessage)Also supports --actions, --resources, --language-model-slug, --temperature, --max-steps, and --wait-until-finished (blocks until the assistant message reaches a terminal status). See references/examples/agents.md for multi-turn conversations, action/resource injection, and model selection.
Inspect records
Records are individual items processed by a workflow. Use these commands to list, count, download, or cancel records within a workflow.
# List records for a workflow
cargo-ai orchestration record list --workflow-uuid <uuid> --limit 50
# Filter by batch or status
cargo-ai orchestration record list --workflow-uuid <uuid> --batch-uuid <uuid> --statuses error
# Count records
cargo-ai orchestration record count --workflow-uuid <uuid>
# Download records as a file
cargo-ai orchestration record download --workflow-uuid <uuid>
# Get per-node execution metrics
cargo-ai orchestration record get-metrics --workflow-uuid <uuid>
# Cancel records
cargo-ai orchestration record cancel --workflow-uuid <uuid> --ids record-id-1,record-id-2Query orchestration history (orchestration query)
Run SQL against orchestration runtime tables — spans, runs, batches, records — with orchestration query execute. Use this for ad-hoc analytics on workflow execution (error rates, throughput, slowest nodes) without the workflow-scoped filters of run get-metrics / run count.
cargo-ai orchestration query execute "SELECT count() FROM runs WHERE status = 'error'"
cargo-ai orchestration query execute "SELECT status, count() FROM batches GROUP BY status"
cargo-ai orchestration query execute "SELECT * FROM spans ORDER BY execution_started_at DESC LIMIT 10"Tables are referenced without a schema prefix — just spans, runs, batches, or records. Workspace scoping is applied automatically. The query is read-only; DDL, table functions, dictionary accessors, and introspection are denied. See references/examples/queries.md for the schemas, example queries, and limits.
Fetch segment data
Retrieve live records from a segment. IMPORTANT: requires --model-uuid (not --segment-uuid). Get the modelUuid from segment list. Filter JSON uses conjonction (not conjunction) — this is intentional.
cargo-ai segmentation segment fetch \
--model-uuid <uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 100 --fetching-offset 0Supports --sort, --enrich, and --sync. See references/filter-syntax.md for the full filter syntax and references/examples/segments.md for filtering, pagination, sorting, enrollment filters, and enrichment.
Managing segments:
# Update a segment's name or filter
cargo-ai segmentation segment update --uuid <segment-uuid> --name "Updated Name"
cargo-ai segmentation segment update --uuid <segment-uuid> --filter '{"conjonction":"and","groups":[...]}'
# Remove a segment (fails if linked to a workflow)
cargo-ai segmentation segment remove <segment-uuid>Use a workflow template
Templates are pre-built node graphs for common automation patterns (enrichment pipelines, CRM syncs, lead scoring). Browse with template list, inspect with template get <slug>, fill in placeholders, validate, and run.
cargo-ai orchestration template list # list available templates
cargo-ai orchestration template get <slug> # get template nodes + configSee references/examples/templates.md for the full guide including placeholder conventions and end-to-end examples.
Validate and test nodes
Always validate custom node graphs before running them.
cargo-ai orchestration node validate --nodes '[...]'
# → { "outcome": "valid" } or { "outcome": "notValid", "invalidNodes": [...] }For debugging, use node compute (dry-run expressions) or node execute (live test, costs credits). For runs that complete with status: success but produce wrong output (wrong branch taken, empty downstream values), use run.executions[].title from run get only as a quick summary — it may be truncated — and read runContext.<nodeSlug> (returned at the top level of the same run get <run-uuid> response) to verify field-level data. See references/troubleshooting.md → "Debugging a workflow run" and references/nodes.md for the full node creation guide, validation error codes, and examples.
Help
Every command supports --help:
cargo-ai orchestration run create --help
cargo-ai orchestration template list --help
cargo-ai orchestration node validate --help
cargo-ai ai message create --help
cargo-ai orchestration query execute --helpAction examples
What is an action?
An action is a single operation you can execute without building a workflow. Use action execute for one record, or action execute-batch for multiple records.
Actions come in four kinds:
| Kind | What it does | Required fields |
|---|---|---|
tool | Run an orchestration tool | toolUuid or templateSlug or releaseUuid |
connector | Call a third-party service | integrationSlug + actionSlug |
agent | Invoke an AI agent | agentUuid or templateSlug or releaseUuid |
native | Run a built-in platform action | actionSlug |
Every action object also requires a config field (use {} for defaults).
When to use actions vs workflows: Actions are for running a single operation without building a workflow graph. If you need to chain multiple operations together (enrichment → scoring → CRM push), userun create --nodesorbatch create --nodesinstead. Seetools.mdfor workflow examples.
---
Execute one action on one record
# Tool action
cargo-ai orchestration action execute \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--data '{"domain":"acme.com"}'
# Connector action
cargo-ai orchestration action execute \
--action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' \
--data '{"domain":"acme.com"}'
# Agent action
cargo-ai orchestration action execute \
--action '{"kind":"agent","agentUuid":"<agent-uuid>","config":{}}' \
--data '{"company":"Acme Corp"}'Returns a run object. Poll with run get <uuid> until terminal, or pass --wait-until-finished:
cargo-ai orchestration action execute \
--action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' \
--data '{"domain":"acme.com"}' \
--wait-until-finishedCustom polling interval (default 5000ms):
cargo-ai orchestration action execute \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--data '{"domain":"acme.com"}' \
--wait-until-finished --polling-interval 2000Response
{
"run": {
"uuid": "run-uuid",
"status": "pending",
"createdAt": "2025-01-15T10:00:00Z"
}
}With --wait-until-finished, the response contains the terminal run state:
{
"run": {
"uuid": "run-uuid",
"status": "success",
"createdAt": "2025-01-15T10:00:00Z",
"finishedAt": "2025-01-15T10:00:05Z"
}
}Status values: pending, running, success, error, cancelled.
---
Execute one action on many records
cargo-ai orchestration action execute-batch \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--records '[{"domain":"acme.com"},{"domain":"globex.com"},{"domain":"initech.com"}]'Returns a batch object. Poll with batch get <uuid> until terminal, or pass --wait-until-finished:
cargo-ai orchestration action execute-batch \
--action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' \
--records '[{"domain":"acme.com"},{"domain":"globex.com"}]' \
--wait-until-finishedWebhook notification
Get notified when the batch completes instead of polling:
cargo-ai orchestration action execute-batch \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--records '[{"domain":"acme.com"},{"domain":"globex.com"}]' \
--webhook-url "https://hooks.example.com/done" \
--webhook-secret "my-secret"Response
{
"batch": {
"uuid": "batch-uuid",
"status": "pending",
"createdAt": "2025-01-15T10:00:00Z"
}
}With --wait-until-finished:
{
"batch": {
"uuid": "batch-uuid",
"status": "success",
"runsCount": 3,
"executedRunsCount": 3,
"failedRunsCount": 0,
"creditsUsedCount": 3,
"createdAt": "2025-01-15T10:00:00Z",
"finishedAt": "2025-01-15T10:00:15Z"
}
}---
Retry configuration
Add a retry object to the action for automatic retries on transient failures:
cargo-ai orchestration action execute \
--action '{
"kind":"connector",
"integrationSlug":"clearbit",
"actionSlug":"company_enrich",
"config":{},
"retry":{"maximumAttempts":3,"initialInterval":1000,"backoffCoefficient":2}
}' \
--data '{"domain":"acme.com"}' \
--wait-until-finished---
Discovering action parameters
To find the right values for each action kind:
# Tool actions — find toolUuid
cargo-ai orchestration tool list
# → Extract .tools[].uuid
# Connector actions — find integrationSlug + actionSlug
cargo-ai connection integration list
cargo-ai connection integration get <slug>
# → Extract actions from the integration
# Agent actions — find agentUuid
cargo-ai ai agent list
# → Extract .agents[].uuid
# Connector actions — find connectorUuid (optional, for authenticated connectors)
cargo-ai connection connector list
# → Extract .connectors[].uuid---
End-to-end: enrich a company with a connector action
# 1. Find the integration and action
cargo-ai connection integration get clearbit
# → Find actionSlug: "company_enrich"
# 2. Execute
cargo-ai orchestration action execute \
--action '{"kind":"connector","integrationSlug":"clearbit","actionSlug":"company_enrich","config":{}}' \
--data '{"domain":"acme.com"}' \
--wait-until-finished
# → Done. Check run.status for success/error.End-to-end: run a tool action on multiple leads
# 1. Find the tool
cargo-ai orchestration tool list
# → Find "Lead Enrichment", extract uuid
# 2. Execute batch
cargo-ai orchestration action execute-batch \
--action '{"kind":"tool","toolUuid":"<tool-uuid>","config":{}}' \
--records '[
{"email":"alice@acme.com","company":"Acme"},
{"email":"bob@globex.com","company":"Globex"},
{"email":"carol@initech.com","company":"Initech"}
]' \
--wait-until-finished
# → Check batch.status, batch.failedRunsCountAI agent examples
Basic chat: ask a question and get a response
# 1. Find the right agent by name
cargo-ai ai agent list
# → Match by name, extract agent uuid
# 2. Create a chat session
cargo-ai ai chat create \
--trigger '{"type":"draft"}' \
--agent-uuid <agent-uuid> \
--name "Quick question"
# → Extract chat.uuid
# 3. Send a message
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"What is Acme Corp'\''s employee count?"}]'Message create response:
{
"userMessage": { "uuid": "user-msg-uuid", "status": "success" },
"assistantMessage": {
"uuid": "assistant-msg-uuid",
"status": "pending",
"parts": []
}
}# 4. Poll for the response (repeat every 2s)
cargo-ai ai message get <assistant-msg-uuid>Poll until status is success or error:
{
"message": {
"uuid": "assistant-msg-uuid",
"status": "success",
"parts": [
{ "type": "text", "text": "Acme Corp has approximately 500 employees..." }
],
"errorMessage": null
}
}Status values: pending → generating → success or error. On error, read .message.errorMessage.
Multi-turn conversation
# 1. Create a chat
cargo-ai ai chat create \
--trigger '{"type":"draft"}' \
--agent-uuid <agent-uuid> \
--name "Lead research"
# → Extract chat.uuid
# 2. First message
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Find the VP of Sales at Acme Corp"}]'
# → Poll assistantMessage.uuid until success
# 3. Follow-up in the same chat (agent remembers context)
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Now find their email address"}]'
# → Poll the new assistantMessage.uuid
# 4. Another follow-up
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Draft a cold outreach email to them"}]'
# → Poll againReuse an existing chat session
# 1. List existing chats for an agent
cargo-ai ai chat list --agent-uuid <agent-uuid> --limit 10
# → Find a chat by name or pick the most recent one
# 2. Send a message in the existing chat
cargo-ai ai message create \
--chat-uuid <existing-chat-uuid> \
--parts '[{"type":"text","text":"Any updates on the Acme deal?"}]'
# → Poll for responseSend a message with actions
Give the agent access to specific actions for enrichment, CRM actions, etc.
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Enrich this lead and add to Salesforce"}]' \
--actions '[{"slug":"clearbit","kind":"tool","toolUuid": "<tool-uuid>","config":{}},{"slug":"salesforce","kind":"tool","config":{}}]'
# → The agent can use these actions during its responseSend a message with model resources
Give the agent access to a data model to query.
# 1. Find the model UUID
cargo-ai storage model list
# 2. Send message with the model as a resource
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Find all companies in France with more than 100 employees"}]' \
--resources '[{"slug":"companies","kind":"model","integrationSlug":"salesforce","modelUuid":"<model-uuid>"}]'Use a specific language model and temperature
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Write a creative subject line for this campaign"}]' \
--language-model-slug gpt-4o \
--temperature 0.9Lower temperature (0.0–0.3) for factual/structured tasks, higher (0.7–1.0) for creative tasks.
Send a message with actions, resources, and custom model
Full example combining all options.
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Research Acme Corp, enrich their data, and update our CRM"}]' \
--actions '[{"slug":"clearbit","kind":"tool","config":{}},{"slug":"salesforce","kind":"tool","config":{}}]' \
--resources '[{"slug":"companies","kind":"model","integrationSlug":"salesforce","modelUuid":"<model-uuid>"}]' \
--language-model-slug gpt-4o \
--temperature 0.3 \
--max-steps 10List messages in a chat
cargo-ai ai message list --chat-uuid <chat-uuid> --limit 20
# → Returns all messages in order (both user and assistant)Check all chats for an agent
# All chats
cargo-ai ai chat list --agent-uuid <agent-uuid>
# With pagination
cargo-ai ai chat list --agent-uuid <agent-uuid> --limit 5 --offset 0End-to-end: use an AI template to create an agent and run a research task
This example uses an AI template to bootstrap a lead researcher agent, then sends it a research task.
# Step 1 — Browse AI templates
cargo-ai ai template list
# → Find slug: "lead-researcher"
# languageModelSlug: "gpt-4o", temperature: 0.3
# Step 2 — Create an agent
cargo-ai ai agent create \
--name "Lead Researcher" \
--icon-color purple --icon-face 🔍
# → Extract agent.uuid (e.g. "agent-abc")
# Step 3 — Configure the draft release with template settings
cargo-ai ai release update-draft --agent-uuid agent-abc \
--system-prompt "You are a research assistant. Given a company domain and a contact name, find their role, LinkedIn profile URL, and email address. Return structured JSON with keys: role, linkedin_url, email." \
--language-model-slug gpt-4o \
--temperature 0.3
# Step 4 — Attach a knowledge file (optional — ICP criteria, product info, etc.)
cargo-ai content file upload --file-path ./icp-criteria.pdf
# → Extract file.uuid
# Step 5 — Give the agent access to actions (optional — connectors as actions)
cargo-ai orchestration tool list
# → Find a "Find Email" tool, extract uuid
# Step 6 — Create a chat session
cargo-ai ai chat create \
--trigger '{"type":"draft"}' \
--agent-uuid agent-abc \
--name "Lead research — Acme Corp"
# → Extract chat.uuid
# Step 7 — Send the research request
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Research the VP of Sales at acme.com. Find their name, LinkedIn URL, and email address."}]' \
--actions '[{"slug":"find_email","kind":"tool","toolUuid":"<email-finder-tool-uuid>","config":{}}]' \
--max-steps 10
# → Extract assistantMessage.uuid
# Step 8 — Poll for the response (every 2s)
cargo-ai ai message get <assistant-msg-uuid>
# → Done when message.status is "success" (read .parts) or "error" (read .errorMessage)
# Step 9 — Follow up in the same chat
cargo-ai ai message create \
--chat-uuid <chat-uuid> \
--parts '[{"type":"text","text":"Now draft a personalised cold outreach email to this person."}]'
# → Poll againPlay examples
What is a play?
A play is a segment-driven automation. It is linked to a specific model and segment, and runs its workflow automatically when records in that segment change (are added, updated, or removed). Plays are the reactive side of Cargo — "when this data changes, do that."
Key properties of a play:
- `name` — human-readable name (workflows themselves don't have names)
- `workflowUuid` — the underlying workflow that executes
- `modelUuid` — the data model the play operates on
- `segmentUuid` — the segment that triggers runs
- `changeKinds` — which segment changes trigger a run (
added,updated,removed) - `schedule` — optional cron schedule for periodic re-evaluation
- `isEnabled` — whether the play is active
List all plays
cargo-ai orchestration play listResponse:
{
"plays": [
{
"uuid": "play-uuid",
"name": "Enrich new companies",
"workflowUuid": "workflow-uuid",
"modelUuid": "model-uuid",
"segmentUuid": "segment-uuid",
"changeKinds": ["added", "updated"],
"isEnabled": true,
"schedule": null,
"description": "Enriches companies when they enter the segment"
}
]
}Find a play's workflow UUID
Plays have names — workflows don't. Use the play to find the right workflow and segment.
# 1. Find the play
cargo-ai orchestration play list
# → Extract play.workflowUuid and play.segmentUuid
# 2. Create a batch using the play's own segment
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"<play.segmentUuid>"}'
# 3. Poll until done
cargo-ai orchestration batch get <batch-uuid>
# Or block until finished — returns the final batch result without a separate poll step
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"<play.segmentUuid>"}' \
--wait-until-finishedUpdate a play's workflow
To change what a play does, update its draft release and deploy it. The draft release holds the unpublished node graph for the workflow.
Looking for inspiration? Before designing a node graph from scratch, checkcargo-ai orchestration template listfor pre-built patterns (lead scoring, enrichment pipelines, CRM syncs). Usecargo-ai orchestration template get <slug>to copy a ready-made node graph and adapt it instead of starting from zero. Templates tagged"kind":"play"are designed for segment-driven automations.
# Step 1 — Find the play and its workflowUuid
cargo-ai orchestration play list
# → Find "Enrich new companies", extract play.workflowUuid
# Step 2 — Get the current draft release (contains the current node graph)
cargo-ai orchestration draft-release get --workflow-uuid <play.workflowUuid>
# → Copy the "nodes" array and make your changes
# Step 3 — Update the draft release with your new nodes
cargo-ai orchestration draft-release update \
--workflow-uuid <play.workflowUuid> \
--nodes '[...your updated node graph...]'
# Step 4 — Validate the updated nodes before deploying
cargo-ai orchestration node validate --nodes '[...your updated node graph...]'
# → { "outcome": "valid" }
# Step 5 — Deploy the draft release
cargo-ai orchestration draft-release deploy \
--workflow-uuid <play.workflowUuid> \
--nodes '[...your updated node graph...]' \
--form-fields 'null' \
--description "Your release description"Do not skip validation. Deploying an invalid node graph will cause runs to fail. Always runnode validatebeforedraft-release deploy.
Do not pass `--version` to `draft-release deploy`. The deploy-specific--versionflag is shadowed by the global--versionflag — passing it causes the command to print the CLI version (e.g.1.0.11) and exit 0 without deploying. Omit it and let the server auto-assign (first deploy →1.0.0, then1.0.1, etc.). Always confirm the deploy worked withrelease get-deployed --workflow-uuid <uuid>— the response should showstatus: "deployed", notdraft.
---
Run a play's workflow on specific records
`run create` is not compatible with play workflows — it will return
playNotCompatible. Always usebatch createfor plays.
>
Allowed batch data kinds for plays:segment,change,filter,recordIds.
By filter (query the model)
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"filter","modelUuid":"<play.modelUuid>","filter":{"field":"domain","operator":"is","value":"acme.com"},"limit":10}'By record IDs
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"recordIds","modelUuid":"<play.modelUuid>","ids":["record-id-1","record-id-2"]}'Monitor a play's runs
# List recent runs
cargo-ai orchestration run list \
--workflow-uuid <play.workflowUuid> \
--limit 20
# Count errors
cargo-ai orchestration run count \
--workflow-uuid <play.workflowUuid> \
--statuses error
# List running batches
cargo-ai orchestration batch list \
--workflow-uuids <play.workflowUuid> \
--statuses runningCancel runs and batches
# Cancel specific runs
cargo-ai orchestration run cancel \
--workflow-uuid <play.workflowUuid> \
--uuids <run-uuid-1>,<run-uuid-2>
# Cancel a batch
cargo-ai orchestration batch cancel <batch-uuid>End-to-end: use a template to run a play
This example takes a "lead-scoring" play template, fills in its placeholders, validates the node graph, and runs it against the play's segment.
# Step 1 — List available play templates
cargo-ai orchestration template list
# → Find slug: "lead-scoring", kind: "play"
# Step 2 — Get the template's node graph
cargo-ai orchestration template get lead-scoring
# → Copy the "nodes" array. It will contain __REPLACE_WITH_*__ placeholders.
# Step 3 — Discover what you need to fill in
cargo-ai connection connector list
# → Find your connector UUIDs (e.g. a Clearbit connector)
cargo-ai ai agent list
# → Find agentUuid if the template uses an agent node
# Step 4 — Validate the node graph after filling in placeholders
cargo-ai orchestration node validate --nodes '[
{
"uuid": "77777777-7777-4777-a777-777777777777", "slug": "start", "kind": "native", "actionSlug": "start",
"config": {}, "childrenUuids": ["88888888-8888-4888-a888-888888888888"], "fallbackOnFailure": false,
"position": {"x": 0, "y": 0}
},
{
"uuid": "88888888-8888-4888-a888-888888888888", "slug": "score", "kind": "native", "actionSlug": "agent",
"config": {
"prompt": {
"kind": "templateExpression",
"expression": "Score this lead from 1-10 based on ICP fit. Company: {{nodes.start.company}}, Domain: {{nodes.start.domain}}, Employee count: {{nodes.start.employee_count}}. Return score and reasoning.",
"instructTo": "none",
"fromRecipe": false
},
"advancedSettings": {
"connectorUuid": "<openai-connector-uuid>",
"languageModelSlug": "gpt-4.1-mini",
"temperature": 0.1
}
},
"childrenUuids": ["99999999-9999-4999-a999-999999999999"], "fallbackOnFailure": false,
"position": {"x": 0, "y": 166}
},
{
"uuid": "99999999-9999-4999-a999-999999999999", "slug": "end", "kind": "native", "actionSlug": "end",
"config": {
"variables": [
{"name": "score", "type": "number", "value": {"kind": "templateExpression", "expression": "{{nodes.score.score}}", "instructTo": "none", "fromRecipe": false}},
{"name": "reasoning", "type": "string", "value": {"kind": "templateExpression", "expression": "{{nodes.score.reasoning}}", "instructTo": "none", "fromRecipe": false}}
]
},
"childrenUuids": [], "fallbackOnFailure": false,
"position": {"x": 0, "y": 332}
}
]'
# → { "outcome": "valid" }
# Step 5 — Find the play's workflowUuid and segmentUuid
cargo-ai orchestration play list
# → Find "Lead Scoring", extract workflowUuid and segmentUuid
# Step 6 — Run the template nodes against the play's segment
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"<play.segmentUuid>"}' \
--nodes '[...validated nodes from step 4...]'
# → Extract batch.uuid
# Step 7 — Poll until finished (every 5s)
cargo-ai orchestration batch get <batch-uuid>
# → Done when .status is "success", "error", or "cancelled"
# Alternative to steps 6+7 — block until finished in one command
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"<play.segmentUuid>"}' \
--nodes '[...validated nodes from step 4...]' \
--wait-until-finishedOrchestration query examples
Run SQL against orchestration runtime tables — runs, batches, spans, records — with cargo-ai orchestration query execute. Use this for ad-hoc analytics on workflow execution (error rates, throughput, slowest nodes, per-node failure breakdowns) without the workflow-scoped filters of run get-metrics / run count.
The backing store is ClickHouse; queries are read-only and exit non-zero with {"errorMessage": "..."} on error.
For SQL against workspace storage (Companies, Contacts, Deals…), usecargo-ai storage query execute "<sql>"— documented in thecargo-storageskill (references/examples/queries.md).
Basic query flow
cargo-ai orchestration query execute \
"SELECT count() FROM runs WHERE status = 'error'"Success response:
{
"rows": [{ "count()": 42 }]
}Tables
Tables are referenced without a schema prefix. The query engine scopes every read to your workspace automatically.
| Table | Use it for |
|---|---|
runs | Per-record workflow executions (status, timing, executions array, batch) |
batches | Batch-level rows: counts (runs_count, failed_runs_count), credit usage |
spans | Flattened per-node execution rows (one row per node execution) |
records | Materialized view over runs keyed by record id |
Common columns: workspace_uuid, workflow_uuid, batch_uuid, release_uuid, status, created_at, updated_at, finished_at, credits_used_count. See the migration files in apps/backend/src/domains/orchestration/migrations/ for the full schema.
Example queries
# Error rate across the whole workspace
cargo-ai orchestration query execute \
"SELECT countIf(status='error') / count() AS error_rate FROM runs WHERE created_at > now() - INTERVAL 1 DAY"
# Errors per workflow over the last week
cargo-ai orchestration query execute \
"SELECT workflow_uuid, count() AS errors FROM runs WHERE status='error' AND created_at > now() - INTERVAL 7 DAY GROUP BY workflow_uuid ORDER BY errors DESC"
# Batch status breakdown
cargo-ai orchestration query execute \
"SELECT status, count() FROM batches GROUP BY status"
# Slowest node executions in the last hour
cargo-ai orchestration query execute \
"SELECT node_slug, node_kind, dateDiff('second', execution_started_at, execution_finished_at) AS duration_s
FROM spans
WHERE execution_finished_at > now() - INTERVAL 1 HOUR
ORDER BY duration_s DESC
LIMIT 20"
# Per-node failure counts
cargo-ai orchestration query execute \
"SELECT node_slug, count() AS failures
FROM spans
WHERE execution_status='error' AND execution_started_at > now() - INTERVAL 1 DAY
GROUP BY node_slug
ORDER BY failures DESC"
# Credit spend by workflow this month
cargo-ai orchestration query execute \
"SELECT workflow_uuid, sum(credits_used_count) AS credits
FROM batches
WHERE created_at >= toStartOfMonth(now())
GROUP BY workflow_uuid
ORDER BY credits DESC"Common table expressions
cargo-ai orchestration query execute \
"WITH recent AS (SELECT * FROM runs WHERE created_at > now() - INTERVAL 1 DAY)
SELECT status, count() FROM recent GROUP BY status"Limits and restrictions
Orchestration queries run as a read-only ClickHouse user with per-query caps:
| Limit | Value |
|---|---|
max_execution_time | 30s |
max_result_rows | 10 000 |
max_rows_to_read | 10 000 000 |
max_columns_to_read | 50 |
max_subquery_depth | 5 |
DDL, introspection functions, table functions (merge, cluster, remote, url, s3, file, …), dictionary accessors, and the query cache are all denied. Wrap heavy aggregations in time filters (created_at > now() - INTERVAL N DAY) to stay under the row-scan cap.
Error handling
{ "errorMessage": "Code: 158. Memory limit exceeded ..." }Common causes:
- Scanned too many rows → narrow the time window with a
created_at/execution_started_atpredicate - Forbidden function (e.g.
system.tables,cluster(),url()) → use onlySELECTagainst the four tables above - Too many result rows → add a
LIMITor aggregate before returning
Segment data examples
Remember: segment fetch and segment download require --model-uuid, not --segment-uuid. Get the modelUuid from segment list.
Remember: filter JSON uses conjonction (not conjunction).
Fetch all records (no filter)
# 1. Find the model UUID
cargo-ai segmentation segment list
# → Extract modelUuid from the segment you want
# 2. Fetch with empty filter
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 100 --fetching-offset 0Response:
{
"records": [
{ "_id": "rec-1", "name": "Acme Corp", "domain": "acme.com", "employee_count": 500 },
{ "_id": "rec-2", "name": "Globex", "domain": "globex.com", "employee_count": 1200 }
],
"count": 2,
"columns": [
{ "slug": "_id", "type": "string", "label": "ID", "modelUuid": "model-uuid" },
{ "slug": "name", "type": "string", "label": "Company Name", "modelUuid": "model-uuid" }
]
}Fetch with pagination
# Page 1
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 50 --fetching-offset 0
# Page 2
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 50 --fetching-offset 50
# Page 3
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 50 --fetching-offset 100Fetch with sorting
# Sort by creation date (newest first)
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--sort '[{"columnSlug":"created_at","kind":"desc"}]' \
--fetching-limit 100
# Sort by employee count (highest first)
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--sort '[{"columnSlug":"employee_count","kind":"desc"}]' \
--fetching-limit 50Filter by string column
# Companies in the US
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "country", "operator": "is", "values": ["US"]}
]
}]
}' \
--fetching-limit 100
# Companies whose name contains "tech"
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "name", "operator": "contains", "values": "tech"}
]
}]
}' \
--fetching-limit 100Filter by number column
# Companies with 100+ employees
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 100}
]
}]
}' \
--fetching-limit 100
# Companies with 50–200 employees
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "number", "columnSlug": "employee_count", "operator": "between", "firstValue": 50, "lastValue": 200}
]
}]
}' \
--fetching-limit 100Filter by date column
# Created after a specific date
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "date", "columnSlug": "created_at", "operator": "greaterThan", "value": "2025-01-01"}
]
}]
}' \
--fetching-limit 100
# Created in a date range
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "date", "columnSlug": "created_at", "operator": "between", "firstValue": "2025-01-01", "lastValue": "2025-03-31"}
]
}]
}' \
--fetching-limit 100Filter by boolean column
# Only customers
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "boolean", "columnSlug": "is_customer", "operator": "isTrue"}
]
}]
}' \
--fetching-limit 100Combine multiple conditions (AND)
# US companies with 100+ employees, created after 2025-01-01
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "country", "operator": "is", "values": ["US"]},
{"kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 100},
{"kind": "date", "columnSlug": "created_at", "operator": "greaterThan", "value": "2025-01-01"}
]
}]
}' \
--sort '[{"columnSlug":"employee_count","kind":"desc"}]' \
--fetching-limit 50Sort by multiple columns
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--sort '[{"columnSlug":"country","kind":"asc"},{"columnSlug":"employee_count","kind":"desc"}]' \
--fetching-limit 100OR logic across groups
# Companies in the US OR companies with 500+ employees
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "or",
"groups": [
{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "country", "operator": "is", "values": ["US"]}
]
},
{
"conjonction": "and",
"conditions": [
{"kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 500}
]
}
]
}' \
--fetching-limit 100Filter for non-null values
# Only records with an email
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "email", "operator": "isNotNull"}
]
}]
}' \
--fetching-limit 100Filter records NOT enrolled in a workflow
Find records that have never been processed by a specific play or tool. First get the workflowUuid from play list or tool list.
# 1. Find the workflow UUID from the play
cargo-ai orchestration play list
# → Extract play.workflowUuid
# 2. Fetch records that have never entered this workflow
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowEntered",
"frequency": {"operator": "not"},
"period": {"operator": "moreThan", "value": 0, "unit": "day"}
}
]
}]
}' \
--fetching-limit 100Filter records enrolled in a workflow in the last 30 days
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowEntered",
"frequency": {"operator": "moreThan", "value": 0},
"period": {"operator": "lessThan", "value": 30, "unit": "day"}
}
]
}]
}' \
--fetching-limit 100Combine enrollment with other conditions
US companies not yet enrolled in the enrichment workflow:
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{
"conjonction": "and",
"groups": [{
"conjonction": "and",
"conditions": [
{"kind": "string", "columnSlug": "country", "operator": "is", "values": ["US"]},
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowEntered",
"frequency": {"operator": "not"},
"period": {"operator": "moreThan", "value": 0, "unit": "day"}
}
]
}]
}' \
--fetching-limit 100Fetch with enrichment and sync
Enrichment triggers any connected enrichment tools on the records. Sync writes the results back to the model.
cargo-ai segmentation segment fetch \
--model-uuid <model-uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--fetching-limit 50 \
--enrich --syncDiscover column slugs before filtering
# List models to see all columns with their slugs and types
cargo-ai storage model list
# → models[].columns[].slug — use these in filter conditions
# → models[].columns[].type — use to pick the right condition kind:
# "string" → kind "string"
# "number" → kind "number"
# "date" → kind "date"
# "boolean" → kind "boolean"
# "object" → kind "object"
# "array" → kind "array"Orchestration templates
What is a template?
A template is a pre-built workflow blueprint — a ready-to-use node graph that captures common automation patterns (enrichment pipelines, CRM syncs, AI research flows, lead scoring). Templates serve two purposes:
1. Design-time inspiration — when building or updating a tool or play, browse templates to find one close to your use case, then copy its node graph into your draft release as a starting point instead of designing from scratch. 2. Runtime shortcut — plug a template's nodes directly into run create or batch create via the --nodes flag without modifying the tool's stored definition.
Templates are read-only. You discover them by slug, inspect their node graph and expected input schema, then either adapt the graph for a draft release or pass it directly as --nodes when creating a run or batch.
List all templates
cargo-ai orchestration template listResponse:
{
"templates": [
{
"slug": "company-enrichment",
"name": "Company Enrichment",
"description": "Enrich a company record with firmographic data from Clearbit",
"kind": "tool"
},
{
"slug": "lead-scoring",
"name": "Lead Scoring",
"description": "Score inbound leads based on ICP fit",
"kind": "play"
}
]
}Key fields:
- `slug` — identifier used to fetch the template
- `name` — human-readable name
- `kind` —
"tool"(on-demand) or"play"(segment-driven)
Get a template by slug
cargo-ai orchestration template get <slug>Example:
cargo-ai orchestration template get company-enrichmentResponse:
{
"template": {
"slug": "company-enrichment",
"name": "Company Enrichment",
"description": "Enrich a company record with firmographic data from Clearbit",
"kind": "tool",
"nodes": [
{
"uuid": "44444444-4444-4444-a444-444444444444",
"slug": "start",
"kind": "native",
"actionSlug": "start",
"config": {},
"childrenUuids": ["55555555-5555-4555-a555-555555555555"],
"fallbackOnFailure": false,
"position": { "x": 0, "y": 0 }
},
{
"uuid": "55555555-5555-4555-a555-555555555555",
"slug": "enrich_company",
"kind": "connector",
"integrationSlug": "clearbit",
"actionSlug": "enrichCompanyFromDomain",
"connectorUuid": "__REPLACE_WITH_CONNECTOR_UUID__",
"config": {
"domain": {
"kind": "templateExpression",
"expression": "{{nodes.start.domain}}",
"instructTo": "none",
"fromRecipe": false
}
},
"childrenUuids": ["66666666-6666-4666-a666-666666666666"],
"fallbackOnFailure": false,
"position": { "x": 0, "y": 166 }
},
{
"uuid": "66666666-6666-4666-a666-666666666666",
"slug": "end",
"kind": "native",
"actionSlug": "end",
"config": {
"variables": [
{
"name": "company_name",
"type": "string",
"value": {
"kind": "templateExpression",
"expression": "{{nodes.enrich_company.name}}",
"instructTo": "none",
"fromRecipe": false
}
}
]
},
"childrenUuids": [],
"fallbackOnFailure": false,
"position": { "x": 0, "y": 332 }
}
]
}
}Use a template as inspiration when building a tool or play
When creating or redesigning a tool or play, start with a template rather than building nodes from scratch. Copy the template's node graph into the draft release, replace any placeholders, then deploy.
# 1. Find a template that matches your use case
cargo-ai orchestration template list
# → Find "company-enrichment" (kind: "tool") or "lead-scoring" (kind: "play")
# 2. Inspect the node graph — understand the structure and spot placeholders
cargo-ai orchestration template get company-enrichment
# 3. Fill in placeholders (connectorUuid, agentUuid, etc.) and validate
cargo-ai orchestration node validate --nodes '[...modified nodes...]'
# → { "outcome": "valid" }
# 4. Find your tool's workflowUuid
cargo-ai orchestration tool list
# → Extract tool.workflowUuid
# 5. Save the adapted nodes to the draft release
cargo-ai orchestration draft-release update \
--workflow-uuid <tool.workflowUuid> \
--nodes '[...validated nodes...]'
# 6. Deploy the draft release
cargo-ai orchestration draft-release deploy \
--workflow-uuid <tool.workflowUuid> \
--nodes '[...validated nodes...]' \
--form-fields 'null' \
--description "Based on company-enrichment template"For plays, the same pattern applies — just useplay listand replacerun createwithbatch createin any test steps.
Use a template to run a tool
The standard pattern:
1. List templates to find the right slug 2. Get the template to inspect its nodes 3. Replace any __REPLACE_WITH_*__ placeholders in the node graph 4. Validate the nodes before running 5. Run against a tool workflow
# 1. Find the template
cargo-ai orchestration template list
# → Find "company-enrichment"
# 2. Get the node graph
cargo-ai orchestration template get company-enrichment
# → Copy the "nodes" array, replace connectorUuid placeholders
# 3. Find your connector UUID
cargo-ai connection connector list
# → Find your Clearbit connector, extract its uuid
# 4. Validate the modified nodes
cargo-ai orchestration node validate --nodes '[...modified nodes...]'
# → { "outcome": "valid" }
# 5. Find the tool
cargo-ai orchestration tool list
# → Find your tool, extract workflowUuid
# 6. Run
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domain":"acme.com"}' \
--nodes '[...validated nodes...]'
# → Poll with: cargo-ai orchestration run get <run-uuid>Use a template to run a play
For kind: "play" templates, use batch create instead of run create:
# 1. Get the template
cargo-ai orchestration template get lead-scoring
# 2. Replace placeholders, validate
cargo-ai orchestration node validate --nodes '[...]'
# 3. Find the play's workflowUuid and segmentUuid
cargo-ai orchestration play list
# 4. Batch run on the play's segment
cargo-ai orchestration batch create \
--workflow-uuid <play.workflowUuid> \
--data '{"kind":"segment","segmentUuid":"<play.segmentUuid>"}' \
--nodes '[...validated nodes...]'
# → Poll with: cargo-ai orchestration batch get <batch-uuid>Placeholder convention
Template node graphs use __REPLACE_WITH_*__ strings to mark values that must be substituted before use:
| Placeholder | Replace with |
|---|---|
__REPLACE_WITH_CONNECTOR_UUID__ | UUID from cargo-ai connection connector list |
__REPLACE_WITH_TOOL_UUID__ | UUID from cargo-ai orchestration tool list |
__REPLACE_WITH_AGENT_UUID__ | UUID from cargo-ai ai agent list |
__REPLACE_WITH_MODEL_UUID__ | UUID from cargo-ai storage model list |
Always run node validate after substitution to confirm there are no structural errors.
Tool examples
What is a tool?
A tool is an on-demand workflow. Unlike plays (which react to segment changes), tools are triggered manually, via API, or on a cron schedule. Tools are the proactive side of Cargo — "run this workflow right now on this data."
Key properties of a tool:
- `name` — human-readable name (workflows themselves don't have names)
- `workflowUuid` — the underlying workflow that executes
- `description` — what the tool does
- `creditsCost` — estimated credit cost per run
- `triggers` — optional cron triggers for scheduled execution
- `isReadOnly` — whether the tool can be modified
List all tools
cargo-ai orchestration tool listResponse:
{
"tools": [
{
"uuid": "tool-uuid",
"name": "Company Enrichment",
"workflowUuid": "workflow-uuid",
"description": "Enriches a company record with firmographic data",
"creditsCost": { "kind": "minMax" },
"triggers": [],
"isReadOnly": false
}
]
}Find a tool's workflow UUID
Tools have names — workflows don't. Use the tool to find the right workflow.
# 1. List tools, find by name
cargo-ai orchestration tool list
# → Find "Company Enrichment", extract tool.workflowUuid
# 2. Use the workflowUuid for run/batch commands
cargo-ai orchestration run create \
--workflow-uuid <workflow-uuid-from-tool> \
--data '{"company":"Acme Corp","domain":"acme.com"}'Update a tool's workflow
To change what a tool does, update its draft release and deploy it. The draft release holds the unpublished node graph for the workflow.
Looking for inspiration? Before designing a node graph from scratch, checkcargo-ai orchestration template listfor pre-built patterns (enrichment pipelines, CRM syncs, AI research flows). Usecargo-ai orchestration template get <slug>to copy a ready-made node graph and adapt it instead of starting from zero. Templates tagged"kind":"tool"are designed for on-demand workflows.
# Step 1 — Find the tool and its workflowUuid
cargo-ai orchestration tool list
# → Find "Company Enrichment", extract tool.workflowUuid
# Step 2 — Get the current draft release (contains the current node graph)
cargo-ai orchestration draft-release get --workflow-uuid <tool.workflowUuid>
# → Copy the "nodes" array and make your changes
# Step 3 — Update the draft release with your new nodes
cargo-ai orchestration draft-release update \
--workflow-uuid <tool.workflowUuid> \
--nodes '[...your updated node graph...]'
# Step 4 — Validate the updated nodes before deploying
cargo-ai orchestration node validate --nodes '[...your updated node graph...]'
# → { "outcome": "valid" }
# Step 5 — Deploy the draft release
cargo-ai orchestration draft-release deploy \
--workflow-uuid <tool.workflowUuid> \
--nodes '[...your updated node graph...]' \
--form-fields 'null' \
--description "Your release description"Do not skip validation. Deploying an invalid node graph will cause runs to fail. Always runnode validatebeforedraft-release deploy.
---
Run a tool on a single record
The most common use case — run an existing tool workflow on one record. Tools support both run create (single record) and batch create (multiple records). Allowed batch data kinds for tools: file, records.
# 1. Find the tool
cargo-ai orchestration tool list
# → Extract tool.workflowUuid
# 2. Run with inline record data
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme Corp","domain":"acme.com","employee_count":500}'
# Or block until finished — returns the final run result without a separate poll step
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme Corp","domain":"acme.com","employee_count":500}' \
--wait-until-finishedRun create response:
{
"run": {
"uuid": "run-uuid",
"workflowUuid": "...",
"status": "pending",
"createdAt": "2025-01-15T10:00:00Z"
}
}# 3. Poll run status every 2s
cargo-ai orchestration run get <run-uuid>Poll until status is success, error, or cancelled:
{
"run": {
"uuid": "run-uuid",
"status": "success",
"createdAt": "...",
"finishedAt": "2025-01-15T10:00:05Z"
}
}Upload a CSV file
Before running a tool on records from a file, you must upload the CSV first. The upload returns the s3Filename needed by batch commands.
cargo-ai workspaceManagement file upload --file-path ./my-companies.csvResponse:
{
"s3Filename": "abc123-my-companies.csv",
"contentType": "text/csv",
"name": "my-companies.csv"
}You can also inspect which columns the file contains:
cargo-ai workspaceManagement file list-columns --s3-filename abc123-my-companies.csvRun a tool on records from a file
# 1. Find the tool
cargo-ai orchestration tool list
# → Extract tool.workflowUuid
# 2. Upload the CSV
cargo-ai workspaceManagement file upload --file-path ./my-companies.csv
# → Extract s3Filename from the response
# 3. Create the batch
cargo-ai orchestration batch create \
--workflow-uuid <tool.workflowUuid> \
--data '{"kind":"file","s3Filename":"<s3Filename>"}'
# → Extract batch.uuid
# 4. Poll until finished (repeat every 5s)
cargo-ai orchestration batch get <batch-uuid>
# → Done when .status is "success", "error", or "cancelled"
# → Extract batch.releaseUuid
# Or skip polling — block until finished and get the final batch result in one step
cargo-ai orchestration batch create \
--workflow-uuid <tool.workflowUuid> \
--data '{"kind":"file","s3Filename":"<s3Filename>"}' \
--wait-until-finished
# → Returns the final batch result directly
# 5. Download results
cargo-ai orchestration batch download \
--uuid <batch-uuid> \
--output-node-slug endMonitor a tool's runs
# List recent runs
cargo-ai orchestration run list \
--workflow-uuid <tool.workflowUuid> \
--limit 20
# Running and pending runs
cargo-ai orchestration run list \
--workflow-uuid <tool.workflowUuid> \
--statuses running,pending
# Error runs
cargo-ai orchestration run list \
--workflow-uuid <tool.workflowUuid> \
--statuses error \
--limit 10
# Count errors
cargo-ai orchestration run count \
--workflow-uuid <tool.workflowUuid> \
--statuses errorMonitor running batches
# List all running batches for the tool
cargo-ai orchestration batch list \
--workflow-uuids <tool.workflowUuid> \
--statuses running
# Check a specific batch
cargo-ai orchestration batch get <batch-uuid>Cancel runs and batches
# Cancel specific runs
cargo-ai orchestration run cancel \
--workflow-uuid <tool.workflowUuid> \
--uuids <run-uuid-1>,<run-uuid-2>
# Cancel a batch (stops all remaining runs)
cargo-ai orchestration batch cancel <batch-uuid>Run with custom nodes (ad-hoc workflow)
The --nodes flag lets you run a custom node graph at runtime without modifying the tool's published definition. When using --nodes, you do not need to pass --workflow-uuid — the nodes define the entire workflow inline. Every graph needs a start node and an end node, linked via childrenUuids.
See ../nodes.md for the full node creation guide — node kinds, native actions, config expressions, routing, and more examples.Minimal example — start, enrich via connector, output:
cargo-ai orchestration run create \
--data '{"domain":"acme.com"}' \
--nodes '[
{
"uuid":"11111111-1111-4111-a111-111111111111","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["22222222-2222-4222-a222-222222222222"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"22222222-2222-4222-a222-222222222222","slug":"enrich_company","kind":"connector",
"integrationSlug":"clearbit","actionSlug":"enrichCompanyFromDomain",
"connectorUuid":"<connector-uuid>",
"config":{
"domain":{"kind":"templateExpression","expression":"{{nodes.start.domain}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["33333333-3333-4333-a333-333333333333"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"33333333-3333-4333-a333-333333333333","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"company_name","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.enrich_company.name}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Validate before running:
cargo-ai orchestration node validate --nodes '[...]'
# → { "outcome": "valid" } or { "outcome": "notValid", "invalidNodes": [...] }Custom node runs are polled the same way as regular runs:
cargo-ai orchestration run get <run-uuid>Common errors
| Error | Cause | Fix |
|---|---|---|
startNodeNotFound | No node with slug:"start" and actionSlug:"start" | Add the required start node |
invalidReleaseOrCustomNodes | Both --release-uuid and --nodes provided | Use one or the other, not both |
nodesNotFound | childrenUuids references a UUID not in the array | Verify all UUID cross-references |
Run a tool multiple times on different records
# Run on first record
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme Corp","domain":"acme.com"}'
# Run on second record
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Globex Inc","domain":"globex.com"}'
# Poll each run separately
cargo-ai orchestration run get <run-uuid-1>
cargo-ai orchestration run get <run-uuid-2>End-to-end: use a template to run a tool
This example takes a "company-enrichment" tool template, fills in its connector placeholder, validates, and runs it on a single record.
# Step 1 — List available tool templates
cargo-ai orchestration template list
# → Find slug: "company-enrichment", kind: "tool"
# Step 2 — Get the template's node graph
cargo-ai orchestration template get company-enrichment
# → Copy the "nodes" array. It contains __REPLACE_WITH_CONNECTOR_UUID__ placeholders.
# Step 3 — Find your connector UUID
cargo-ai connection connector list
# → Find your Clearbit connector, extract uuid (e.g. "abc-123")
# Step 4 — Fill in placeholders and validate
cargo-ai orchestration node validate --nodes '[
{
"uuid": "44444444-4444-4444-a444-444444444444", "slug": "start", "kind": "native", "actionSlug": "start",
"config": {}, "childrenUuids": ["55555555-5555-4555-a555-555555555555"], "fallbackOnFailure": false,
"position": {"x": 0, "y": 0}
},
{
"uuid": "55555555-5555-4555-a555-555555555555", "slug": "enrich_company", "kind": "connector",
"integrationSlug": "clearbit", "actionSlug": "enrichCompanyFromDomain",
"connectorUuid": "abc-123",
"config": {
"domain": {
"kind": "templateExpression",
"expression": "{{nodes.start.domain}}",
"instructTo": "none",
"fromRecipe": false
}
},
"childrenUuids": ["66666666-6666-4666-a666-666666666666"], "fallbackOnFailure": false,
"position": {"x": 0, "y": 166}
},
{
"uuid": "66666666-6666-4666-a666-666666666666", "slug": "end", "kind": "native", "actionSlug": "end",
"config": {
"variables": [
{"name": "company_name", "type": "string", "value": {"kind": "templateExpression", "expression": "{{nodes.enrich_company.name}}", "instructTo": "none", "fromRecipe": false}},
{"name": "industry", "type": "string", "value": {"kind": "templateExpression", "expression": "{{nodes.enrich_company.category.industry}}", "instructTo": "none", "fromRecipe": false}},
{"name": "employees", "type": "string", "value": {"kind": "templateExpression", "expression": "{{nodes.enrich_company.metrics.employeesRange}}", "instructTo": "none", "fromRecipe": false}}
]
},
"childrenUuids": [], "fallbackOnFailure": false,
"position": {"x": 0, "y": 332}
}
]'
# → { "outcome": "valid" }
# Step 5 — (Optional) Preview expression resolution without side effects
cargo-ai orchestration node compute \
--node '{"uuid":"55555555-5555-4555-a555-555555555555","slug":"enrich_company","kind":"connector","integrationSlug":"clearbit","actionSlug":"enrichCompanyFromDomain","connectorUuid":"abc-123","config":{"domain":{"kind":"templateExpression","expression":"{{nodes.start.domain}}","instructTo":"none","fromRecipe":false}},"childrenUuids":["66666666-6666-4666-a666-666666666666"],"fallbackOnFailure":false,"position":{"x":0,"y":166}}' \
--context '{"nodes":{"start":{"domain":"acme.com"}}}'
# → Shows resolved config: { "domain": "acme.com" }
# Step 6 — Find the tool's workflowUuid
cargo-ai orchestration tool list
# → Find "Company Enrichment", extract workflowUuid
# Step 7 — Run
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domain":"acme.com"}' \
--nodes '[...validated nodes from step 4...]'
# → Extract run.uuid
# Step 8 — Poll until done (every 2s)
cargo-ai orchestration run get <run-uuid>
# → Done when status is "success", "error", or "cancelled"Filter syntax
Complete reference for building segment filter conditions in the Cargo CLI.
CRITICAL — common silent failure:
Every filter object uses the keyconjonction— notconjunction.
This is intentional (French spelling). A typo here does not throw an error — it simply returns no records.
Double-check this spelling every time you write a filter. Search for conjunction in your JSON before running.Structure
A filter has two levels of nesting: top-level groups joined by a conjunction, and each group contains conditions joined by their own conjunction.
{
"conjonction": "and",
"groups": [
{
"conjonction": "and",
"conditions": [
{ "kind": "string", "columnSlug": "domain", "operator": "contains", "values": "acme" }
]
}
]
}- Top-level
conjonction:"and"or"or"— joins the groups - Group-level
conjonction:"and"or"or"— joins the conditions within a group - Empty filter (all records):
{"conjonction":"and","groups":[]}
Condition kinds and operators
string
{ "kind": "string", "columnSlug": "name", "operator": "is", "values": ["Acme Corp"] }
{ "kind": "string", "columnSlug": "name", "operator": "isNot", "values": ["Test"] }
{ "kind": "string", "columnSlug": "name", "operator": "contains", "values": "acme" }
{ "kind": "string", "columnSlug": "name", "operator": "doesNotContain", "values": "test" }
{ "kind": "string", "columnSlug": "name", "operator": "startsWith", "values": "A" }
{ "kind": "string", "columnSlug": "name", "operator": "endsWith", "values": "Corp" }
{ "kind": "string", "columnSlug": "name", "operator": "isNull" }
{ "kind": "string", "columnSlug": "name", "operator": "isNotNull" }
{ "kind": "string", "columnSlug": "name", "operator": "isEmpty" }
{ "kind": "string", "columnSlug": "name", "operator": "isNotEmpty" }Operators with values: is, isNot, contains, doesNotContain, startsWith, endsWith. values can be a string or an array of strings.
Operators without values: isNull, isNotNull, isEmpty, isNotEmpty.
number
{ "kind": "number", "columnSlug": "employee_count", "operator": "is", "value": 500 }
{ "kind": "number", "columnSlug": "employee_count", "operator": "isNot", "value": 0 }
{ "kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 100 }
{ "kind": "number", "columnSlug": "employee_count", "operator": "lowerThan", "value": 1000 }
{ "kind": "number", "columnSlug": "employee_count", "operator": "between", "firstValue": 100, "lastValue": 500 }
{ "kind": "number", "columnSlug": "employee_count", "operator": "isNull" }
{ "kind": "number", "columnSlug": "employee_count", "operator": "isNotNull" }Note: single-value operators use value (not values). between uses firstValue and lastValue.
date
{ "kind": "date", "columnSlug": "created_at", "operator": "is", "value": "2025-01-15" }
{ "kind": "date", "columnSlug": "created_at", "operator": "isNot", "value": "2025-01-15" }
{ "kind": "date", "columnSlug": "created_at", "operator": "greaterThan", "value": "2025-01-01" }
{ "kind": "date", "columnSlug": "created_at", "operator": "lowerThan", "value": "2025-06-01" }
{ "kind": "date", "columnSlug": "created_at", "operator": "between", "firstValue": "2025-01-01", "lastValue": "2025-06-30" }
{ "kind": "date", "columnSlug": "created_at", "operator": "isNull" }
{ "kind": "date", "columnSlug": "created_at", "operator": "isNotNull" }Same structure as number but value/firstValue/lastValue are ISO date strings.
boolean
{ "kind": "boolean", "columnSlug": "is_customer", "operator": "isTrue" }
{ "kind": "boolean", "columnSlug": "is_customer", "operator": "isFalse" }
{ "kind": "boolean", "columnSlug": "is_customer", "operator": "isNull" }
{ "kind": "boolean", "columnSlug": "is_customer", "operator": "isNotNull" }object / array
{ "kind": "object", "columnSlug": "metadata", "operator": "isNull" }
{ "kind": "object", "columnSlug": "metadata", "operator": "isNotNull" }
{ "kind": "object", "columnSlug": "metadata", "operator": "matchConditions" }For matchConditions, nest objectProperty conditions inside.
objectProperty
Used to filter on nested properties within object or array columns:
{
"kind": "objectProperty",
"columnSlug": "metadata",
"propertyName": "industry",
"operator": "is",
"value": "SaaS"
}Supports: is, isNot, contains, doesNotContain, startsWith, endsWith, greaterThan, lowerThan, between, isNull, isNotNull, isEmpty, isNotEmpty.
For between: use value and otherValue.
segment
Filter records that belong (or don't belong) to another segment:
{ "kind": "segment", "operator": "in", "segmentUuid": "other-segment-uuid" }
{ "kind": "segment", "operator": "notIn", "segmentUuid": "other-segment-uuid" }enrollment
Filter records based on whether they have been enrolled (or not) in a workflow. Useful to find records that have never been processed by a play/tool.
Records NOT enrolled in a workflow (never entered):
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowEntered",
"frequency": { "operator": "not" },
"period": { "operator": "moreThan", "value": 0, "unit": "day" }
}Records enrolled more than 3 times:
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowEntered",
"frequency": { "operator": "moreThan", "value": 3 },
"period": { "operator": "moreThan", "value": 0, "unit": "day" }
}Records that left a workflow in the last 30 days:
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowLeft",
"frequency": { "operator": "moreThan", "value": 0 },
"period": { "operator": "lessThan", "value": 30, "unit": "day" }
}Records where a specific node was executed:
{
"kind": "enrollment",
"workflowUuid": "<workflow-uuid>",
"activityKind": "workflowNodeExecuted",
"nodeSlug": "enrich_company",
"frequency": { "operator": "moreThan", "value": 0 },
"period": { "operator": "moreThan", "value": 0, "unit": "day" }
}activityKind values: workflowEntered, workflowNodeExecuted, workflowLeft.
frequency.operator values: not (never), moreThan, lessThan, exactly.
period.operator values: moreThan, lessThan, exactly. unit is always "day".
nodeSlug is optional — only used with workflowNodeExecuted.
occurrence
Filter records based on related model activity (e.g. a contact's company has certain events).
{
"kind": "occurrence",
"relatedModelUuid": "<related-model-uuid>",
"frequency": { "operator": "moreThan", "value": 0 },
"period": { "operator": "lessThan", "value": 30, "unit": "day" },
"conjonction": "and",
"conditions": [
{ "kind": "string", "columnSlug": "event_type", "operator": "is", "values": ["meeting_booked"] }
]
}Same frequency and period syntax as enrollment. The conditions array can contain any string/number/date/boolean conditions to filter the related model's records.
sql
Raw SQL clause (advanced):
{ "kind": "sql", "name": "custom_filter", "clause": "revenue > 1000000 AND country = 'US'" }Complete example
Filter for companies with 100+ employees whose name contains "tech", created after 2025-01-01:
{
"conjonction": "and",
"groups": [
{
"conjonction": "and",
"conditions": [
{ "kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 100 },
{ "kind": "string", "columnSlug": "name", "operator": "contains", "values": "tech" },
{ "kind": "date", "columnSlug": "created_at", "operator": "greaterThan", "value": "2025-01-01" }
]
}
]
}OR logic
Filter for companies in the US OR with 500+ employees:
{
"conjonction": "or",
"groups": [
{
"conjonction": "and",
"conditions": [
{ "kind": "string", "columnSlug": "country", "operator": "is", "values": ["US"] }
]
},
{
"conjonction": "and",
"conditions": [
{ "kind": "number", "columnSlug": "employee_count", "operator": "greaterThan", "value": 500 }
]
}
]
}Sort syntax
Sort is an array of sort objects. Each object has columnSlug and kind.
[{"columnSlug": "created_at", "kind": "desc"}]columnSlug— the column slug to sort by (frommodel list→columns[].slug)kind—"asc"(ascending) or"desc"(descending)
Multiple sort columns (first by country ascending, then by employee count descending):
[{"columnSlug": "country", "kind": "asc"}, {"columnSlug": "employee_count", "kind": "desc"}]Usage with --sort:
cargo-ai segmentation segment fetch \
--model-uuid <uuid> \
--filter '{"conjonction":"and","groups":[]}' \
--sort '[{"columnSlug":"created_at","kind":"desc"}]' \
--fetching-limit 100Tips
- Get available column slugs from
cargo-ai storage model list→columns[].slug - Use the column
typeto pick the right conditionkind(string → string, number → number, etc.) - An empty filter
{"conjonction":"and","groups":[]}returns all records relatedModelUuidis optional on conditions — only needed for cross-model filters
Prefer built-in actions + expressions over code/HTTP nodes
When building a workflow, use the actions Cargo already provides plus template expressions. Avoid `python`, `script` (JavaScript), and raw HTTP nodes unless you genuinely have no other option.
Code and raw-HTTP nodes feel flexible, but they are the hardest part of a workflow to build and debug from the CLI: they fail in ways the native nodes don't, and you can't see inside them as easily. Most of what they get used for is already a one-line native node or a template expression.
Use this instead
| Instead of writing… | Use |
|---|---|
python / script to reshape, rename, or extract fields | a variables node — each value is a template expression, e.g. {{nodes.start.email.split('@')[1]}} |
python / script to call an LLM and parse its JSON | the native agent node with output.type:"jsonSchema" — it returns structured JSON, no parsing (read it as {{nodes.<slug>.answer.<field>}}) |
| a raw HTTP request | the integration's dedicated connector action (e.g. clearbit.enrichCompanyFromDomain) — discover them with connection integration get-documentation <slug> |
python / script to decide a path | filter / branch / switch with a boolean expression |
python / script to loop over a list | a group node |
time.sleep() to wait | a delay node |
Template expressions cover most "transforms"
Inside {{ }} you can do property/index access, string and number operations, and boolean logic — so field extraction and conditions belong in a variables node or a condition, not in code:
{{nodes.start.email.split('@')[1]}}
{{nodes.enrich.metrics.employeesRange}}
{{nodes.start.employee_count > 100}}One caveat: a reference to a missing path resolves to empty silently (the run still says success). When a value comes out blank, check the real shape with cargo-ai orchestration run get <run-uuid> → runContext.<slug> (node outputs are returned by the CLI) and fix the path.
When a code or HTTP node is genuinely warranted
- Multi-step computation that no expression or native node expresses (messy
parsing, dedup, aggregating a group node's array into one object).
- An API with no dedicated connector action.
If you do need code, prefer the JS script node for transforms (it ships lodash for array/object work). Either way, both code nodes are sandboxed and have no normal logging — return your output and inspect it via runContext.
Creating nodes
What is a custom node graph?
A node graph is a directed acyclic graph of steps that defines a workflow. Each graph must have exactly one start node (entry point) and one end node (exit point). Intermediate nodes perform actions — enrichments, transformations, branching, AI calls, etc. — and are linked together via childrenUuids.
Pass a custom node graph to override a tool's deployed release:
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domain":"acme.com"}' \
--nodes '[...]'Also works with batch create --nodes. Cannot be combined with --release-uuid.
Always validate first — use node validate to catch structural errors before running:
cargo-ai orchestration node validate --nodes '[...]'Node shape
Every node in the --nodes JSON array has these fields:
| Field | Required | Description |
|---|---|---|
uuid | yes | Unique ID within the graph — must be a valid UUIDv4 (e.g. "550e8400-e29b-41d4-a716-446655440000") |
slug | yes | Human-readable identifier (start and end are reserved) |
kind | yes | native, connector, tool, or agent |
config | yes | Action-specific configuration ({} for start) |
childrenUuids | yes | UUIDs of downstream nodes — array length must match the childrenCount for the node's action (see native actions tables below) |
fallbackOnFailure | yes | Continue to the next node even if this one fails |
position | yes | {"x": 0, "y": 0} — layout only, no runtime effect |
fallbackChildUuid | no | UUID of a fallback node to run on failure |
retry | no | {"maximumAttempts": 3, "initialInterval": 1000, "backoffCoefficient": 2} |
name | no | Display name |
description | no | Description |
Node kinds
Each kind requires additional fields beyond the common shape.
native
Built-in workflow actions (start, end, branch, filter, variables, etc.).
| Field | Required | Description |
|---|---|---|
actionSlug | yes | Which native action to run |
connector
Third-party integration actions (Clearbit, HubSpot, HTTP, etc.). Discover identifiers first:
cargo-ai connection integration list # → integrationSlug
cargo-ai connection integration get-documentation <slug> # → actionSlug + config fields
cargo-ai connection connector list # → connectorUuid| Field | Required | Description |
|---|---|---|
integrationSlug | yes | Integration identifier (e.g. clearbit) |
actionSlug | yes | Action within the integration |
connectorUuid | yes | Your connected account UUID |
childrenCount for connector nodes equals the integration action's children array length if defined, otherwise defaults to 1. Most connector actions have exactly 1 child.
Config values from autocomplete: When you run integration get <slug>, some actions include a uiSchema alongside the jsonSchema. If a field has "ui:widget": "IntegrationAutocompleteWidget" in the uiSchema, you must fetch its allowed values using connector autocomplete rather than guessing or using freeform input. See cargo-connection/SKILL.md for the full autocomplete workflow.
tool
Embeds another tool (sub-workflow) as a node. The tool's deployed release config fields become the node's config.
| Field | Required | Description |
|---|---|---|
toolUuid | no | Target tool UUID — get from cargo-ai orchestration tool list |
templateSlug | no | Template slug — use when instantiating from a template |
releaseUuid | no | Pin to a specific release of the tool |
Provide at least one of toolUuid or templateSlug. childrenCount is 1.
# Find toolUuid
cargo-ai orchestration tool list
# → Extract tool.uuidagent
Embeds a saved AI agent as a node. The agent runs to completion and its output is available to downstream nodes.
| Field | Required | Description |
|---|---|---|
agentUuid | no | Target agent UUID — get from cargo-ai ai agent list |
templateSlug | no | Template slug — use when instantiating from a template |
releaseUuid | no | Pin to a specific release of the agent |
Provide at least one of agentUuid or templateSlug. childrenCount is 1.
# Find agentUuid
cargo-ai ai agent list
# → Extract agent.uuidThe config for an agent node takes two fields:
| Field | Description |
|---|---|
prompt | The user message sent to the agent — string or expression object |
output | How to parse the agent's response — { type: "text" } or { type: "jsonSchema", jsonSchema: {...} } |
prompt can be a plain string or an expression object:
"prompt": "Summarize the company {{nodes.start.domain}}""prompt": {
"kind": "templateExpression",
"expression": "Classify {{nodes.start.company}} into a category",
"instructTo": "none",
"fromRecipe": false
}output is a discriminated union on type:
output.type | Additional field | Description |
|---|---|---|
"text" | (none) | Returns the agent's raw text response |
"jsonSchema" | jsonSchema — a JSON Schema object | Forces structured JSON output matching the schema |
"output": { "type": "text" }"output": {
"type": "jsonSchema",
"jsonSchema": {
"type": "object",
"properties": {
"category": { "type": "string" },
"confidence": { "type": "number" }
},
"required": ["category", "confidence"],
"additionalProperties": false
}
}Reading agent output downstream — the `.answer` wrapper. The parsed JSON is exposed under.answer, not directly on the node. Use{{nodes.<slug>.answer.<field>}}(e.g.{{nodes.classify.answer.category}}). Referencing{{nodes.classify.category}}resolves to undefined silently — branch conditions evaluate to false, end-node variables come out empty, and the run still reportssuccess. This bites hard because nothing errors. Same path applies tooutput.type: "text"results — read them via{{nodes.<slug>.answer}}.
Config values and expressions
Config fields that need dynamic data use expression objects:
{
"kind": "templateExpression",
"expression": "{{nodes.start.domain}}",
"instructTo": "none",
"fromRecipe": false
}| Field | Values | Description |
|---|---|---|
kind | "templateExpression", "jsExpression" | Template expressions use {{...}} syntax; JS expressions are raw JS |
expression | string | The expression to evaluate |
instructTo | "none", "ai" | "none" = JS evaluation; "ai" = AI fills the value from the expression as an instruction |
fromRecipe | boolean | false for inline expressions |
Data flow
Each node's output is stored under its slug. Reference it in downstream nodes with {{nodes.<slug>.<field>}}.
{{nodes.start.domain}} — input data field
{{nodes.enrich_company.name}} — output from the "enrich_company" node
{{nodes.enrich_company.metrics.employeesRange}} — nested field access
{{nodes.start.email.split('@')[1]}} — JS expressions work inside {{ }}Inside a group loop, use {{parentNodes.<slug>.<field>}} to access the parent run's context.
Static values
For config fields that don't need expressions, pass the value directly:
{ "minutes": 5 }Native actions reference
These are the actionSlug values available for kind: "native" nodes.
Workflow entry/exit
| actionSlug | Purpose | childrenCount | Config |
|---|---|---|---|
start | Entry point | 1 | {} |
end | Exit point, define output | 0 | {"variables": [{"name", "type", "value"}]} |
The end node's variables array defines the workflow output. Each variable has:
name— output field nametype—"string","number","boolean","date","array","object", or"any"value— an expression object or static value
Routing
| actionSlug | Purpose | childrenCount | Config |
|---|---|---|---|
filter | Continue only if true | 1 | {"filter": <bool-expression>} |
branch | If/else split | 2 | {"condition": <bool-expression>} |
switch | Multi-way routing | dynamic | {"routes": [{"name": "...", "uuid": "...", "value": <bool-expression>}]} |
split | Random A/B split | 2 | {"percentage": <0-100>} |
`childrenUuids` ordering matters:
- `branch`: index 0 = condition matched ("yes"), index 1 = not matched ("no")
- `filter`: index 0 = condition true (execution stops if false — no child called)
- `switch`: first route whose
valueevaluates totruewins; its index in theroutesarray determines whichchildrenUuidsentry to follow - `split`: index 0 = random number < percentage ("A"), index 1 = otherwise ("B")
Data
| actionSlug | Purpose | childrenCount | Config |
|---|---|---|---|
variables | Create/transform data | 1 | {"variables": [{"name": "...", "type": "...", "value": <expression>}]} |
Same shape as end variables, but the output is available to downstream nodes via {{nodes.<slug>.<name>}}.
Flow control
| actionSlug | Purpose | childrenCount | Config |
|---|---|---|---|
delay | Wait before next node | 1 | {"minutes": <number>} |
group | Loop over array items | 1 | {"items": <array-expression>, "failOnItemFailure": false, "_nodes": [...]} |
The group node iterates over items, running the child subgraph once per item. Each iteration can access the current item via {{nodes.start.value}} (for simple values) or {{nodes.start.<field>}} (for object items). Use {{parentNodes.<slug>.<field>}} to reference the parent run's data.
Reading group results downstream: the group node's output is an array, one entry per iteration, where each entry is that iteration's final (end) node output. Access it by index:{{nodes.<groupSlug>[0].<field>}}. There is no `.results` wrapper —{{nodes.<groupSlug>.results[0]...}}does not work — and arrow-function array methods like{{nodes.<groupSlug>.map(x => x.field)}}are not supported in template expressions. To collapse the array into one value, use ascriptnode withlodash(or apythonnode). See `node-selection.md` → "Group node results".
`delay` and context: prior node outputs are not lost across adelay— the full run context is checkpointed and restored, so{{nodes.<slug>...}}still resolves after the delay regardless of node kind. The checkpoint is JSON, though, so values you read after a delay must be JSON-serializable. Materialize anything you need post-delay into avariablesnode (plain strings/numbers/objects) before the delay rather than relying on apythonnode'sresult. See `node-selection.md` → "What survives adelayboundary".
Group sub-graph (`_nodes`): The _nodes array inside the group's config defines the internal workflow executed for each item. It follows the exact same rules as a top-level node graph:
- Must have a
startnode and anendnode - Every node's
childrenUuidsmust contain exactly the number of entries shown in the `childrenCount` column of the native actions tables above (e.g.start→ 1,variables→ 1,branch→ 2,end→ 0). This rule applies identically at both the top level and inside_nodes - The
endnode must havechildrenUuids: [] - Reference the current item with
{{nodes.start.value}}or{{nodes.start.<field>}} - Reference parent workflow data with
{{parentNodes.<slug>.<field>}}
Important: Do NOT leave the last sub-node with childrenUuids: [] unless it is the end node. Every other node type (variables, connector, tool, agent, etc.) requires exactly 1 child. Always terminate the sub-graph with an explicit end node.
AI and code
| actionSlug | Purpose | childrenCount | Config |
|---|---|---|---|
agent | Inline AI agent | 1 | {"prompt": "...", "advancedSettings": {"connectorUuid": "...", "languageModelSlug": "gpt-4.1-mini"}} |
python | Run Python code | 1 | {"script": "..."} |
script | Run JavaScript code | 1 | {"script": "..."} |
The agent action requires advancedSettings.connectorUuid (an AI provider connector — get it from connector list). Optional fields: actions, resources, capabilities, output (structured output with {"type": "jsonSchema", "jsonSchema": {...}}), advancedSettings.temperature, advancedSettings.maxSteps, advancedSettings.systemPrompt.
The python and script nodes receive nodes and parentNodes as context variables. The return value of the script becomes the node's output under {{nodes.<slug>.result}} (assign to a variable named result in Python; return a value in JS).
Prefer built-in actions + expressions over code nodes. Before adding apythonorscriptnode, read `node-selection.md`: most transforms belong in avariablesnode, LLM calls in the nativeagentnode, API calls in the integration's connector action, and routing inbranch/filter/switch. Reach for code only for genuine multi-step computation (prefer the JSscriptnode — it shipslodash).
Examples
Connector node: enrich then output
# 1. Discover integration, action, and connector
cargo-ai connection integration list
cargo-ai connection integration get-documentation clearbit
cargo-ai connection connector list
# 2. Run with custom nodes
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domain":"acme.com"}' \
--nodes '[
{
"uuid":"11111111-1111-4111-a111-111111111111","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["22222222-2222-4222-a222-222222222222"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"22222222-2222-4222-a222-222222222222","slug":"enrich_company","kind":"connector",
"integrationSlug":"clearbit","actionSlug":"enrichCompanyFromDomain",
"connectorUuid":"<connector-uuid>",
"config":{
"domain":{"kind":"templateExpression","expression":"{{nodes.start.domain}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["33333333-3333-4333-a333-333333333333"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"33333333-3333-4333-a333-333333333333","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"company_name","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.enrich_company.name}}","instructTo":"none","fromRecipe":false}},
{"name":"employee_count","type":"number","value":{"kind":"templateExpression","expression":"{{nodes.enrich_company.metrics.employeesRange}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Tool node: call a sub-tool
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"first_name":"Jane","last_name":"Doe","company_domain":"acme.com"}' \
--nodes '[
{
"uuid":"aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"bbbbbbbb-bbbb-4bbb-bbbb-bbbbbbbbbbbb","slug":"find_email","kind":"tool",
"toolUuid":"<email-finder-tool-uuid>",
"config":{
"first_name":{"kind":"templateExpression","expression":"{{nodes.start.first_name}}","instructTo":"none","fromRecipe":false},
"last_name":{"kind":"templateExpression","expression":"{{nodes.start.last_name}}","instructTo":"none","fromRecipe":false},
"company_domain":{"kind":"templateExpression","expression":"{{nodes.start.company_domain}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["cccccccc-cccc-4ccc-accc-cccccccccccc"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"cccccccc-cccc-4ccc-accc-cccccccccccc","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"email","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.find_email.email}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Branch node: if/else routing
Route based on employee count — enrich large companies, skip small ones.
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domain":"acme.com","employee_count":500}' \
--nodes '[
{
"uuid":"d1d1d1d1-d1d1-4d1d-ad1d-d1d1d1d1d1d1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["d2d2d2d2-d2d2-4d2d-ad2d-d2d2d2d2d2d2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"d2d2d2d2-d2d2-4d2d-ad2d-d2d2d2d2d2d2","slug":"check_size","kind":"native","actionSlug":"branch",
"config":{
"condition":{"kind":"templateExpression","expression":"{{nodes.start.employee_count > 100}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["d3d3d3d3-d3d3-4d3d-ad3d-d3d3d3d3d3d3","d4d4d4d4-d4d4-4d4d-ad4d-d4d4d4d4d4d4"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"d3d3d3d3-d3d3-4d3d-ad3d-d3d3d3d3d3d3","slug":"enrich","kind":"connector",
"integrationSlug":"clearbit","actionSlug":"enrichCompanyFromDomain",
"connectorUuid":"<connector-uuid>",
"config":{
"domain":{"kind":"templateExpression","expression":"{{nodes.start.domain}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["d5d5d5d5-d5d5-4d5d-ad5d-d5d5d5d5d5d5"],"fallbackOnFailure":false,
"position":{"x":-200,"y":332}
},
{
"uuid":"d4d4d4d4-d4d4-4d4d-ad4d-d4d4d4d4d4d4","slug":"skip","kind":"native","actionSlug":"end",
"config":{"variables":[
{"name":"status","type":"string","value":"skipped_too_small"}
]},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":200,"y":332}
},
{
"uuid":"d5d5d5d5-d5d5-4d5d-ad5d-d5d5d5d5d5d5","slug":"end","kind":"native","actionSlug":"end",
"config":{"variables":[
{"name":"company_name","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.enrich.name}}","instructTo":"none","fromRecipe":false}},
{"name":"status","type":"string","value":"enriched"}
]},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":-200,"y":498}
}
]'childrenUuids[0] (d3d3d3d3-...) is the "yes" path, childrenUuids[1] (d4d4d4d4-...) is the "no" path.
Filter + variables: transform and continue conditionally
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"email":"jane@acme.com","company":"Acme Corp"}' \
--nodes '[
{
"uuid":"e1e1e1e1-e1e1-4e1e-ae1e-e1e1e1e1e1e1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["e2e2e2e2-e2e2-4e2e-ae2e-e2e2e2e2e2e2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"e2e2e2e2-e2e2-4e2e-ae2e-e2e2e2e2e2e2","slug":"has_email","kind":"native","actionSlug":"filter",
"config":{
"filter":{"kind":"templateExpression","expression":"{{nodes.start.email !== undefined && nodes.start.email !== null}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["e3e3e3e3-e3e3-4e3e-ae3e-e3e3e3e3e3e3"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"e3e3e3e3-e3e3-4e3e-ae3e-e3e3e3e3e3e3","slug":"extract","kind":"native","actionSlug":"variables",
"config":{
"variables":[
{"name":"domain","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.start.email.split('@')[1]}}","instructTo":"none","fromRecipe":false}},
{"name":"company","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.start.company}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":["e4e4e4e4-e4e4-4e4e-ae4e-e4e4e4e4e4e4"],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
},
{
"uuid":"e4e4e4e4-e4e4-4e4e-ae4e-e4e4e4e4e4e4","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"domain","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.extract.domain}}","instructTo":"none","fromRecipe":false}},
{"name":"company","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.extract.company}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":498}
}
]'If email is null/undefined the filter stops execution — no downstream nodes run.
Agent node: inline AI with structured output
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"company":"Acme Corp","website":"https://acme.com"}' \
--nodes '[
{
"uuid":"f1f1f1f1-f1f1-4f1f-af1f-f1f1f1f1f1f1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["f2f2f2f2-f2f2-4f2f-af2f-f2f2f2f2f2f2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"f2f2f2f2-f2f2-4f2f-af2f-f2f2f2f2f2f2","slug":"classify","kind":"native","actionSlug":"agent",
"config":{
"prompt":{"kind":"templateExpression","expression":"Classify the company {{nodes.start.company}} ({{nodes.start.website}}) into one of these categories: SaaS, E-commerce, Marketplace, Services, Hardware, Other. Return the category and a one-sentence reasoning.","instructTo":"none","fromRecipe":false},
"output":{
"type":"jsonSchema",
"jsonSchema":{
"type":"object",
"properties":{
"category":{"type":"string","enum":["SaaS","E-commerce","Marketplace","Services","Hardware","Other"]},
"reasoning":{"type":"string"}
},
"required":["category","reasoning"],
"additionalProperties":false
}
},
"advancedSettings":{
"connectorUuid":"<openai-connector-uuid>",
"languageModelSlug":"gpt-4.1-mini",
"temperature":0.3,
"maxSteps":5
}
},
"childrenUuids":["f3f3f3f3-f3f3-4f3f-af3f-f3f3f3f3f3f3"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"f3f3f3f3-f3f3-4f3f-af3f-f3f3f3f3f3f3","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"category","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.classify.answer.category}}","instructTo":"none","fromRecipe":false}},
{"name":"reasoning","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.classify.answer.reasoning}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Python node: custom data transformation
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"name":"ACME CORP","domain":" Acme.COM "}' \
--nodes '[
{
"uuid":"a1a1a1a1-a1a1-4a1a-aa1a-a1a1a1a1a1a1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["a2a2a2a2-a2a2-4a2a-aa2a-a2a2a2a2a2a2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"a2a2a2a2-a2a2-4a2a-aa2a-a2a2a2a2a2a2","slug":"normalize","kind":"native","actionSlug":"python",
"config":{
"script":"name = nodes[\"start\"][\"name\"]\ndomain = nodes[\"start\"][\"domain\"]\nresult = {\"name\": name.strip().title(), \"domain\": domain.strip().lower()}"
},
"childrenUuids":["a3a3a3a3-a3a3-4a3a-aa3a-a3a3a3a3a3a3"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"a3a3a3a3-a3a3-4a3a-aa3a-a3a3a3a3a3a3","slug":"end","kind":"native","actionSlug":"end",
"config":{
"variables":[
{"name":"name","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.normalize.result.name}}","instructTo":"none","fromRecipe":false}},
{"name":"domain","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.normalize.result.domain}}","instructTo":"none","fromRecipe":false}}
]
},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Python scripts receive nodes and parentNodes dicts. Set result to define the node output, accessible via {{nodes.<slug>.result}}.
Group node: loop over items
Process each item in an array through a sub-workflow. The group node creates a child batch, running the inner graph once per item. The _nodes array inside the group config defines the sub-graph executed per item — it must have its own start and end nodes, just like a top-level workflow.
cargo-ai orchestration run create \
--workflow-uuid <tool.workflowUuid> \
--data '{"domains":["acme.com","globex.com","initech.com"]}' \
--nodes '[
{
"uuid":"b1b1b1b1-b1b1-4b1b-ab1b-b1b1b1b1b1b1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["b2b2b2b2-b2b2-4b2b-ab2b-b2b2b2b2b2b2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"b2b2b2b2-b2b2-4b2b-ab2b-b2b2b2b2b2b2","slug":"loop","kind":"native","actionSlug":"group",
"config":{
"items":{"kind":"templateExpression","expression":"{{nodes.start.domains}}","instructTo":"none","fromRecipe":false},
"failOnItemFailure":false,
"_nodes":[
{
"uuid":"b2a1a1a1-a1a1-4a1a-aa1a-a1a1a1a1a1a1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["b2a2a2a2-a2a2-4a2a-aa2a-a2a2a2a2a2a2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"b2a2a2a2-a2a2-4a2a-aa2a-a2a2a2a2a2a2","slug":"enrich","kind":"connector",
"integrationSlug":"clearbit","actionSlug":"enrichCompanyFromDomain",
"connectorUuid":"<connector-uuid>",
"config":{
"domain":{"kind":"templateExpression","expression":"{{nodes.start.value}}","instructTo":"none","fromRecipe":false}
},
"childrenUuids":["b2a3a3a3-a3a3-4a3a-aa3a-a3a3a3a3a3a3"],"fallbackOnFailure":false,
"position":{"x":0,"y":160}
},
{
"uuid":"b2a3a3a3-a3a3-4a3a-aa3a-a3a3a3a3a3a3","slug":"end","kind":"native","actionSlug":"end",
"config":{"variables":[
{"name":"company_name","type":"string","value":{"kind":"templateExpression","expression":"{{nodes.enrich.name}}","instructTo":"none","fromRecipe":false}}
]},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":320}
}
]
},
"childrenUuids":["b3b3b3b3-b3b3-4b3b-ab3b-b3b3b3b3b3b3"],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
},
{
"uuid":"b3b3b3b3-b3b3-4b3b-ab3b-b3b3b3b3b3b3","slug":"end","kind":"native","actionSlug":"end",
"config":{"variables":[]},
"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":332}
}
]'Each iteration receives the current item as its start data ({{nodes.start.value}} for simple values, {{nodes.start.<field>}} for object items). Use {{parentNodes.start.domains}} inside the loop to access the parent run's data.
Sub-graph rules: The _nodes array is a complete node graph — it must have start and end nodes. Every intermediate node must chain to the next via childrenUuids. The end node is the only node that should have childrenUuids: [].
Validation
Always validate before running. The command checks for structural errors (missing start/end, broken UUID references, invalid connectors, etc.).
cargo-ai orchestration node validate \
--nodes '[
{
"uuid":"c1c1c1c1-c1c1-4c1c-ac1c-c1c1c1c1c1c1","slug":"start","kind":"native","actionSlug":"start",
"config":{},"childrenUuids":["c2c2c2c2-c2c2-4c2c-ac2c-c2c2c2c2c2c2"],"fallbackOnFailure":false,
"position":{"x":0,"y":0}
},
{
"uuid":"c2c2c2c2-c2c2-4c2c-ac2c-c2c2c2c2c2c2","slug":"end","kind":"native","actionSlug":"end",
"config":{"variables":[]},"childrenUuids":[],"fallbackOnFailure":false,
"position":{"x":0,"y":166}
}
]'Success:
{ "outcome": "valid" }Error:
{
"outcome": "notValid",
"invalidNodes": [
{
"node": { "uuid": "22222222-2222-4222-a222-222222222222", "slug": "enrich_company" },
"reason": "connectorNotFound"
}
]
}Common validation errors
| Error | Cause | Fix |
|---|---|---|
startNodeNotFound | No node with slug:"start" and actionSlug:"start" | Add the required start node |
invalidReleaseOrCustomNodes | Both --release-uuid and --nodes provided | Use one or the other, not both |
nodesNotFound | childrenUuids references a UUID not in the array | Verify all UUID cross-references |
childrenUuidsInvalid | Wrong number of entries in childrenUuids | Check the childrenCount column in the native actions tables — each node type requires an exact count (e.g. variables needs 1, end needs 0, branch needs 2). Inside a group's _nodes, the last node must be an end node (childrenUuids: []), not a variables or connector node with an empty array |
subNodesInvalid | A group node's _nodes sub-graph has invalid nodes | Check the nested invalidNodes array for details — the sub-graph must follow the same rules as a top-level graph (start + end nodes, correct childrenUuids counts) |
connectorNotFound | connectorUuid doesn't match an active connector | Check connector list for the UUID |
nativeInvalid | actionSlug doesn't match a known native action | Check the native actions table |
toolInvalid | toolUuid doesn't match an existing tool | Check tool list for the UUID |
slugInvalid | Slug contains non-word characters | Use only [a-zA-Z0-9_] |
Node compute
node compute evaluates a node's config expressions against a context without executing any side effects — no API calls, no credits consumed. Use it to preview what a node's config will resolve to before running it.
cargo-ai orchestration node compute \
--node '{
"uuid": "22222222-2222-4222-a222-222222222222",
"slug": "enrich_company",
"kind": "connector",
"integrationSlug": "clearbit",
"actionSlug": "enrichCompanyFromDomain",
"connectorUuid": "<connector-uuid>",
"config": {
"domain": {
"kind": "templateExpression",
"expression": "{{nodes.start.domain}}",
"instructTo": "none",
"fromRecipe": false
}
},
"childrenUuids": ["33333333-3333-4333-a333-333333333333"],
"fallbackOnFailure": false,
"position": {"x": 0, "y": 166}
}' \
--context '{"nodes": {"start": {"domain": "acme.com"}}}'The --context object mirrors what nodes receive at runtime — nodes.<slug>.<field> for data from previous nodes. Inside a group loop, also pass groupContext.
Response shows the resolved config values that would be sent to the connector or action.
Don't use `node compute` to debug branch/condition logic. The local evaluator does not reliably resolvetemplateExpressionreferences against--contextfor boolean conditions — literals ({{true}}) work, but{{nodes.qualify.answer.qualified}}may returnfalseeven when the context hasqualified: true. For branch debugging, prefer running the full graph with a single record (batch create --data '{"kind":"recordIds",...}') and inspectingrun get <run-uuid>— readrun.executions[].nodeChildIndex/nextNodeUuidto see which branch was taken, and readrunContext.<upstreamSlug>(returned at the top level of the same response) to verify the field the condition reads.executions[].titleis only a truncated summary. Seereferences/troubleshooting.md→ "Debugging a workflow run".
Node execute
node execute runs a single node in isolation with real side effects — it makes the actual API call (connector, tool, or agent). Use it to test one node without running the full workflow.
Note: node execute consumes credits. It is a live execution, not a dry run.cargo-ai orchestration node execute \
--workflow-uuid <tool.workflowUuid> \
--node '{
"uuid": "22222222-2222-4222-a222-222222222222",
"slug": "enrich_company",
"kind": "connector",
"integrationSlug": "clearbit",
"actionSlug": "enrichCompanyFromDomain",
"connectorUuid": "<connector-uuid>",
"config": {
"domain": {
"kind": "templateExpression",
"expression": "{{nodes.start.domain}}",
"instructTo": "none",
"fromRecipe": false
}
},
"childrenUuids": [],
"fallbackOnFailure": false,
"position": {"x": 0, "y": 166}
}' \
--computed-config '{
"domain": "acme.com"
}' \
--context '{"nodes": {"start": {"domain": "acme.com"}}}'`--computed-config` — the already-resolved config values (output of node compute). If you skip this, the CLI resolves expressions from --context automatically.
`--release-uuid` — optional; pins the execution to a specific workflow release.
Recommended debug workflow
1. Validate structure — node validate --nodes '[...]' — catches structural errors 2. Preview expressions — node compute --node '{...}' --context '{...}' — check resolved values 3. Test live — node execute --node '{...}' --computed-config '{...}' --context '{...}' — confirm real output 4. Run full graph — run create --nodes '[...]' — execute the complete workflow
Polling
Custom node runs are polled the same way as regular runs:
cargo-ai orchestration run get <run-uuid>
# Poll every 2s until status is success, error, or cancelledRelated skills
How it compares
Pick cargo-orchestration over cargo-storage when the task is running workflows and batches rather than inspecting model DDL or workspace storage schemas.
FAQ
What is the difference between Cargo plays and tools?
cargo-orchestration explains that plays are segment-driven automations reacting to CRM data changes, while tools are on-demand workflows triggered manually or via API. run create only works with tool workflows; play workflows require batch create with a segmentUuid.
How do you poll async Cargo orchestration jobs?
cargo-orchestration documents polling run get every 2 seconds for runs and batch get every 5 seconds for batches until status reaches success, error, or cancelled. Developers can pass --wait-until-finished to block until terminal state instead of manual polling.