Skip to main content
Flows are directed acyclic graphs of typed nodes. The flow executor runs the graph, streams events to the agent worker over LiveKit data channels, and persists session state in PostgreSQL.

Execution model

The executor is stateless per request — every invocation reconstructs state from PostgreSQL. A long conversation runs as many short executor calls, driven by inbound events (transcripts, tool callbacks, child-flow completions). This makes scale-out straightforward: any backend pod can pick up the next event.

Node types

16 node types organised by purpose:
NodePurpose
startEntry point, initial variable bindings
endTerminal node, completes the session
messageNon-spoken log entry in the transcript
NodePurpose
speakSends text through TTS to the caller
listenAwaits user utterance; captures transcript into a variable
NodePurpose
conditionBranches on guard expressions over variables
waitDelay-based pause
pauseExplicit pause awaiting operator resume
disconnectTerminates the call
NodePurpose
agenticLLM-driven multi-route decision node
llmSingle-shot LLM call (non-agentic)
toolExecutorInvokes a configured tool (HTTP / webhook / MCP)
NodePurpose
integrationCalls a Nango-backed integration action
ragRetrieval-augmented search over a knowledge base
handoffTransfers the call (bridge to another number)
invokeInvokes a sub-flow in a child room; parent suspends
returnReturns a value to the parent flow after invocation

Parallel branches

branch nodes split execution into N concurrent branches within one executor call. Branches can signal one another within the request; state doesn’t cross request boundaries.

Invoke → return (sub-flows)

A parent flow can invoke a child flow, suspending until the child completes: Useful for: pre-screening / qualification flows, hand-offs to a specialised assistant, retry-with-different-model patterns.

Storage + versioning

Flows are stored as JSON, schema-validated at write time. Versioning is application-level — every save creates a new version row. Flows can be exported via the API for Git-tracked operator workflows.

Channels

ChannelTransportStatus
PhoneSIP via LiveKit SIP + telephony providerimplemented
WhatsAppMeta Cloud APIimplemented
Web widgetnot implemented
Channel-specific prompt hints are injected into the LLM context. Voice flows get SSML / inline-tag guidance; chat flows get markdown guidance — same flow definition, different rendering hint.

Tools

The toolExecutor node supports:
TypeHow it’s authored
Custom TypeScript handlersRegistered in the tool registry
cURL importPaste a cURL command; AICO infers the schema
OpenAPI importPoint at an OpenAPI spec
MCP discovery + testRegister an MCP server, browse its tools
Webhook callbackHMAC-signed callback for asynchronous tools
Tool secrets are scoped per organization, injected at invocation time.

Memory

Three layers, all backed by pgvector cosine similarity:
  • Episodic memory — long-term semantic chunks of user turns. Each turn can be chunked, embedded, and stored with a TTL and optional cluster tag.
  • Persistent user facts — extracted after each turn. Newer extractions supersede older ones.
  • Knowledge bases — per-org document collections; ingestion chunks + embeds, retrieval through the rag node. Queries parallelise across sub-queries.

Sessions

Every flow run is persisted with full variable state, tool-call log, and final status. Operators can list, inspect, export, end, or resume sessions through the dashboard or the REST API (see API reference).