lounge.

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
Mail 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

  1. An integration is a BiFunction with its config bound at build time — one implementation, many nodes.
  2. ${field} from the input record, ${env.VAR} from the environment. Secrets come from the environment, never the spec.
  3. Outbound integrations are steps; inbound bridges start flows. A cron tick is just an event.
  4. Validate integration configuration at build time. Invalid required configuration prevents startup.
  5. 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.md for the full catalogue and the custom-integration recipe; tools/lounge/references/*-integration.md per family.