Octov0.4.2
Core Concepts

Connectors and Blocks

Trigger connectors, service connectors, blocks, and composites — the building model.

Everything in a flow is built from two kinds of parts: connectors, which own a piece of the outside world, and blocks, which process messages. This page explains how they relate and how a flow wires them together.

Connectors

A connector owns an external resource — a socket, a client, a connection pool — and acquires it on start, releases it on stop. You declare instances under connectors: with a name, a type, and settings, then reference them by name.

Connectors serve flows in two ways, and some do both:

Trigger connectors provide a source that feeds messages into a flow:

TypeTriggers a flow when
httpAn HTTP request arrives on a route; the flow's result becomes the response.
cronA schedule fires (six-field cron or @every descriptors).
eventsA message is broadcast on a topic subject (fan-out: every subscriber gets a copy).
queueA message lands on a queue subject (competing consumers: exactly one gets it).

Service connectors provide blocks that a flow calls mid-pipeline:

TypeProvides
http-clientThe rest block — outbound HTTP with base URL, auth, retries.
databaseThe sql block — queries against Postgres or SQLite.
loggerThe log block — structured log output.
slackBlocks to send messages, look up data, and verify inbound events.
notionBlocks to retrieve pages, query data sources, and verify webhooks.
llm-anthropic, llm-openai, llm-geminiThe ai-mapping block and the AI composites (ai-router, ai-agent, ai-retry).

cron, queue, and events need no per-instance settings, so a source can use them without a connectors: entry — the runtime starts a default instance on demand. See implicit connectors.

Blocks

A block is one step in a flow's process chain: it receives a message, transforms it or performs a side effect, and passes it on. Built-in blocks like set-payload, set-variable, and template-resource need nothing external. Connector-backed blocks name the instance they use with a connector: setting (the log block uses logger:):

connectors:
  - name: payments
    type: http-client
    settings:
      baseURL: https://api.payments.example

flows:
  - name: charge
    process:
      - type: rest                  # block provided by the http-client connector
        settings:
          connector: payments       # binds to the instance declared above
          method: POST
          path: /charges
          body: '{"amount": body.amount}'

The binding is by instance name, so two rest blocks can call two different APIs by pointing at two http-client instances.

Composites

Composite blocks nest other blocks through typed slots — this is how you get branching, iteration, parallelism, and error boundaries. They are built directly by the flow builder rather than registered as leaf blocks, and their slots are flow fields, not settings:

CompositeSlotsWhat it does
ifcondition, then / elseRun one branch or the other on a CEL condition.
switchcases / defaultFirst case whose when guard matches wins.
foreachitems, as, bodyRun the body once per array element.
forkbranchesRun branches concurrently on clones, then join.
enrichbody, setBody / setVarsRun the body on an isolated copy, fold back only what you name.
handle-errorsprocess / errorAn error boundary with an inline recovery chain.
validaterules, onRejectAssert CEL rules; reject the message when one fails.
cache-scopekey, ttl, bodyMemoize the body's result with a TTL.
ai-router, ai-agent, ai-retryroutes / tools / processLLM-driven routing, tool-calling agents, self-healing retries.
mcp-routertools, resources, promptsServe flows as an MCP server behind an HTTP source.
process:
  - type: if
    condition: 'body.amount >= 1000.0'
    then:
      process:
        - type: set-variable
          settings: { name: priority, value: '"high"' }
    else:
      process:
        - type: set-variable
          settings: { name: priority, value: '"normal"' }

Slots contain sub-flows, and sub-flows contain blocks — including more composites — so pipelines nest to any depth.

Reuse with named processors

Declare a block once under processors: and reference it from any flow with ref:. The reference takes the definition's type and settings; block-level settings override key by key.

hello-world.yaml (excerpt)
processors:
  - name: greeter
    type: log
    settings:
      level: info
      message: '"hello world! the date is " + body.date'

flows:
  - name: greet
    source:
      connector: ticker
      type: cron
      settings:
        schedule: "0,30 * * * * *"
        payload: '{"date": string(now)}'
    process:
      - ref: greeter

Named processors mirror connectors: both are declared once at the top level and referenced by name, so shared configuration lives in exactly one place.

The complete catalogue

This page covers the model; the Reference documents every connector, source, block, and composite with its full settings.

On this page