Integrations
Most applications eventually cross a process boundary: a step reads a database, calls an API, sends email, invokes a model, or writes an object to storage. This page describes how those external interactions are represented in the tree.
The integration mechanism is the configured form of the task node introduced on page one.
The shape
Recall the two function shapes a task node can carry. A plain task is a
Function<In, Out>. A task that needs options is a
BiFunction<Config, In, Out>, where the first parameter is bound once when the
node is built and the second is the payload arriving on every event.
Integrations use the second form: reusable behavior with node-specific configuration bound during construction.
A SQL query implementation, for example, accepts a statement and datasource as configuration and an input record as its event payload. Different configuration values produce distinct query nodes without requiring distinct implementations:
one implementation many nodes
────────────────── ──────────
┌─▶ find-customer (config: SELECT … WHERE id = ?)
SqlQueryTaskNode ────────┼─▶ recent-orders (config: SELECT … WHERE date > ?)
└─▶ open-invoices (config: SELECT … WHERE paid = false)
An integration therefore uses the ordinary node mechanism; its reusable part is the implementation, and its per-node differences reside in configuration.
The built-in integrations
| Family | Nodes | For |
|---|---|---|
| SQL | SqlTaskNode — operation: query | execute | upsert | proc | command, inferred from the statement when omitted |
any JDBC database — selects, DML, upserts, stored procedures, admin statements |
| PostgreSQL | PostgresPersistenceTaskNode, PostgresRetrievalTaskNode |
the batteries-included path: persist a record, retrieve by session |
| HTTP | RestClientTaskNode |
calling somebody else's API |
MailerTaskNode |
outbound email | |
| Object storage | S3TaskNode — operation: put | get | delete | presign |
files, and pre-signed URLs for handing files to a browser |
| Models | LlmTaskNode |
any OpenAI-compatible chat completion — hosted or local |
| Telemetry | OtelMetricTaskNode |
emitting metrics from inside a flow |
| Broadcast | WebSocketBroadcastNode |
pushing to connected clients |
Each appears as a type: and config: block in the specification. The builder
resolves the implementation and binds its configuration.
- type: RestClientTaskNode
id: fetch-rates
config:
method: GET
url: https://api.example.com/rates/${currency}
inputType: domain.RateRequest
outputType: domain.RateResponse
Templating
In the example, ${currency} is resolved against the input record. Across the
integrations, ${fieldName} is resolved
against the input record, and ${env.VAR} against the environment — in URLs,
in mail subjects and bodies, in prompts.
Using the same substitution rules across integration families keeps their configuration vocabulary consistent.
Environment templating also separates secrets from the specification. API keys
and passwords are supplied through the deployment environment rather than
committed in project.yaml.
Outbound and inbound
External interaction has two directions, and only one is represented by a task node.
Outbound integrations are steps inside a flow. The node makes an external call and the flow continues with the result.
Inbound bridges start flows. The scheduler is the clearest example: it is not a node at all, it is a bridge that fires an event into the tree on a cron or an interval.
lounge.scheduler.enabled=true
lounge.scheduler.cron=0 0 2 * * ?
The tree's root must accept the tick's input type. From that point the tick is processed as an ordinary event with an event group and a route through the tree. A scheduled reconciliation and an HTTP request therefore differ at ingress, not in execution semantics.
The HTTP front door is the other inbound path, and page one already described where its answers come from.
Custom integrations
Applications may define an integration that is not present in the built-in catalogue by implementing the same extension interface used by the built-ins.
The class supplies three things: the type: name it handles, configuration
validation, and construction of a node from that configuration. The engine
discovers it at startup, and from then on the type resolves like a built-in.
Validation runs at build time, so a missing webhook URL stops the application
from starting rather than failing on the first call. The integrations guide
has the interface and a worked Slack example.
The boundary
The boundary between an integration and a task node is a design decision.
An integration performs the external interaction; it does not make the application's decision. A rate integration fetches rates, while a task node decides how to use them. A mail integration sends a prepared message, while a task node determines its recipients. Keeping those decisions in ordinary task nodes leaves them testable without a network and visible as steps in the tree.
Templates and conditionals in integration configuration should therefore be limited to preparing the external call. If they encode business policy, that policy is no longer visible as a separate step in the tree and is harder to test independently.
Some rules to remember
- An integration is a
BiFunctionwith its config bound at build time — one implementation, many nodes. ${field}from the input record,${env.VAR}from the environment. Secrets come from the environment, never the spec.- Outbound integrations are steps; inbound bridges start flows. A cron tick is just an event.
- Validate integration configuration at build time. Invalid required configuration prevents startup.
- Integrations perform external interactions; task nodes make application decisions. Business policy in integration configuration is not visible as a separate step in the map.
Where to go next
- Durability, Clustering and HA — what happens to in-flight work when a process goes away.
- Building, Operating and Securing — observing integrations in operation and securing their entry points.
- Deep reference:
docs/guide/writing-integrations.mdfor the full catalogue and the custom-integration recipe;tools/lounge/references/*-integration.mdper family.