Configuration

Flowgen reads a single YAML file (passed via --config). Top-level sections:

flows:             # Flow discovery (required).
cache:             # Distributed cache backend (optional).
resources:         # External resource loading (optional).
http_server:       # Webhook / metrics HTTP server (optional).
mcp_server:        # MCP server for tools/resources/prompts (optional).
ai_gateway:        # OpenAI-compatible LLM gateway (optional).
web:               # Web UI (optional).
health:            # k8s liveness/readiness listener (defaults on).
retry:             # Default retry policy for every task (optional).
telemetry:         # OpenTelemetry export (optional).

Only flows is required. Every other section has working defaults.

Minimal config

flows:
  path: /etc/flowgen/flows/

Discovers every .yaml/.yml/.json file under the path and runs them with an in-memory cache, no resources directory, no HTTP server, and no telemetry.

Full reference

flows:
  path: /etc/flowgen/flows/
  cache:
    enabled: true
    prefix: flows
    db_name: flowgen_system

cache:
  enabled: true
  type: nats
  credentials_path: /etc/nats/credentials.json
  url: "{{env.NATS_URL}}"
  runtime:
    db_name: flowgen_cache
  system:
    db_name: flowgen_system
  history: 10
  tombstone_ttl: "1h"

resources:
  path: /etc/flowgen/resources/

http_server:
  enabled: true
  port: 3000
  path: /api/flowgen/workers/v1
  credentials_path: /etc/flowgen/credentials/http.json
  auth:
    type: jwt
    secret: "shared-secret"

mcp_server:
  enabled: true
  port: 3001
  path: /mcp/v1
  credentials_path: /etc/flowgen/credentials/mcp.json

ai_gateway:
  enabled: true
  port: 3002
  path: /v1
  credentials_path: /etc/flowgen/credentials/ai.json

web:
  enabled: true
  port: 8080
  path: /flowgen
  headers:
    X-Flowgen-Client: flowgen-ui

health:
  enabled: true
  port: 8081

retry:
  max_attempts: 10
  initial_backoff: "1s"

telemetry:
  enabled: true
  backend:
    type: remote
    endpoint: http://otel-collector:4317
  service_name: flowgen
  metrics_export_interval: "60s"

flows

Flow discovery. Filesystem and distributed cache can be used independently or together; flowgen merges flows from both sources at startup.

FieldTypeDefaultDescription
pathstringDirectory or glob pattern. Filesystem-mode loads every .yaml/.yml/.json file recursively. Omit to skip filesystem loading.
cacheobjectCache-mode flow loading. Loads flows from the metadata cache in addition to the filesystem.
cache.enabledboolrequiredEnable cache-mode flow loading.
cache.prefixstringflowsCache key prefix for flow entries.
cache.db_namestringflowgen_systemCache bucket holding the flow definitions.

Both sources can be active at the same time. Use this for gradual migration: a small set of bootstrap flows mounted from disk (typically a sync flow that pulls user flows from Git into the cache), with the bulk of user flows loaded from the cache. On a name collision the filesystem entry wins, so a locally mounted bootstrap flow cannot be silently overridden by a stale cache entry.

Cache-mode flow loading is useful when flows are provisioned dynamically — for example, a control plane writes flow YAML into NATS KV and every flowgen replica picks them up.

cache

Distributed cache backend. When omitted, flowgen uses an in-memory cache (single-node only).

FieldTypeDefaultDescription
enabledboolrequiredSet false to fall back to in-memory cache.
typestringrequiredBackend type. Currently nats.
credentials_pathstringoptionalPath to NATS credentials file.
urlstringlocalhost:4222NATS server URL.
runtime.db_namestringflowgen_cacheKV bucket for flow state, reachable from scripts via ctx.cache.
system.db_namestringflowgen_systemKV bucket for leader-election leases and peer registration. Kept apart from runtime so scripts cannot reach coordination state. Created whenever the cache is enabled.
historyint64Historical entries retained per key. Only applies when the bucket is created. Server caps at 64.
tombstone_ttlduration1hTTL for delete/purge tombstones. Required for per-key TTLs on cache entries to work.

If NATS is configured but unreachable, flowgen falls back to in-memory automatically and logs a warning. See Caching.

resources

External resource loading for SQL queries, prompts, scripts, schemas. See Resources.

FieldTypeDefaultDescription
pathstringFilesystem base directory for resources. Resource keys resolve relative to this path.
cacheobjectCache-backed resources. When cache.enabled is true, resources load from the cache.
cache.enabledboolrequiredEnable cache-backed resources.
cache.prefixstringresourcesCache key prefix for resource entries.
cache.db_namestringflowgen_systemCache bucket holding resource entries.

When resources is omitted, only inline content is supported — any task that uses { resource: ... } will fail at startup with a clear error.

http_server

HTTP server that hosts every http_endpoint route plus health/metrics endpoints.

FieldTypeDefaultDescription
enabledboolrequiredRequired for http_endpoint tasks to start.
portint3000Listening port.
pathstringOptional path prefix applied to every registered route.
credentials_pathstringShared bearer/basic credentials. Tasks override per-route. See Credentials.
authobjectUser-level authentication provider (JWT, OIDC, session). See Authentication.

mcp_server

MCP server for exposing mcp_tool, mcp_resource, and mcp_prompt tasks to LLM clients.

FieldTypeDefaultDescription
enabledboolrequiredRequired for any MCP task to register.
portint3001Listening port.
pathstring/mcp/v1MCP endpoint path.
credentials_pathstringShared API key credentials. Individual tools can override.
authobjectUser-level authentication provider (JWT, OIDC, session). See Authentication.
resource_uri_schemestringflowgenScheme used when auto-generating mcp_resource URIs of the form <scheme>://<flow_name>/<name>. White-label deployments override this.

ai_gateway

OpenAI-compatible LLM gateway that serves every registered llm_proxy flow.

FieldTypeDefaultDescription
enabledboolrequiredRequired for llm_proxy tasks to be registered.
portint3002Listening port, independent of the webhook HTTP server.
pathstring/v1Path prefix for AI gateway routes. The chat completions endpoint is served at <path>/chat/completions.
credentials_pathstringPath to global credentials file. Individual llm_proxy tasks can override.
authobjectUser-level authentication provider (JWT, OIDC, session). See Authentication.

web

Embedded web dashboard and JSON API.

FieldTypeDefaultDescription
enabledboolrequiredSet true to start the server.
portint8080Listening port.
pathstring/Path prefix for both the UI and the API.
headersmap of string to string{}HTTP headers sent with every request the web server makes to the AI gateway for the Agents tab. Set this so llm_proxy/mcp_tool tasks scoped with a matching headers field can identify and allow the web server as a caller — see AI Gateway and MCP.
agents.enabledboolrequiredShows the Agents tab and enables /api/agents. Omit agents to hide the tab.
agents.conversation_history_ttlduration30dHow long a conversation is kept after its last message; 0 keeps it indefinitely.
agents.cache_prefixstringagentsCache key prefix for conversations, read state and folders: segments of letters, digits, - and _ joined by .. A flow opens a conversation under <cache_prefix>.conversations..
agents.reply_save_intervalduration3sHow often a reply in progress is saved; above 0. A pending reply not saved for ten times this long is shown as interrupted.
agents.reply_idle_timeoutduration10mHow long a reply may wait for the AI gateway’s next output before it fails; above 0.
agents.on_behalf_of_userboolfalseCalls the AI gateway as the signed-in user, with their login token as the bearer. The gateway’s auth must be oidc with the issuer of web.auth and its client_id as the audience.
ai_gateway_urlstringsame-process ai_gatewayBase URL of the AI gateway the Agents tab replies through, e.g. http://127.0.0.1:3002/v1. Set when the AI gateway runs in another process or pod.
api_credentials_pathstringMachine keys the API accepts as Authorization: Bearer next to the login session. See Authoring.
frame_ancestorslist of string["'self'"]Who may show the web UI in a frame, as Content-Security-Policy frame-ancestors sources, e.g. https://console.example.com.
authoring.enabledboolrequiredEnables change proposals. See Authoring.
authoring.targetslistone workspace target over flows/ and resources/Where changes can be proposed. See Targets.
authoring.targets[].namestringrequiredName shown on the target’s changes.
authoring.targets[].pathslist of stringrequiredWorkspace folders the target covers, e.g. flows/user/.
authoring.targets[].publish_flowstringsystem/publish_workspaceIdentity of the flow, starting with an inproc_endpoint, that publishes the target’s approved changes.
authoring.targets[].approver_groupslist of string[]Identity provider groups whose members may approve and reject the target’s changes. Empty allows every signed-in user.
authoring.targets[].track_deploymentbooltrueReports when a committed change runs on this instance. Turn off when the target is not deployed here automatically.
authoring.groups_claimstringgroupsClaim of the signed-in user that lists their groups.

The API contract is defined in openapi.yaml and served at <path>/api/openapi.yaml.

EndpointDescription
GET <path>/api/flowsList loaded flows with live counters.
GET <path>/api/flows/{name}Full YAML source of one flow.
GET <path>/api/flows/streamServer-Sent Events of live per-flow metrics: one snapshot frame with every flow’s counters on connect, then a snapshot frame per change. Event/log history and live tail come from /api/logs and /api/logs/stream instead.
GET <path>/api/logsRetained log records — framework, lifecycle, and per-task activity. Accepts ?limit= (default 500, max 10000) and ?flow= to scope to one flow’s records.
GET <path>/api/logs/streamServer-Sent Events live tail of log records as they arrive, same ?flow= scoping as /api/logs.
GET <path>/api/resourcesList discoverable resources.
GET <path>/api/resources/{key}Fetch one resource’s content.
GET <path>/api/versionRunning build version.
GET <path>/api/configNon-secret config info shown in the web UI (e.g. whether the Agents chat is configured).
GET <path>/api/agents/modelsProxies GET /models on the AI gateway for the built-in Agents chat’s model picker.
GET <path>/api/agents/toolsEach mcp_tool with its flow and the connectors that flow reaches.
GET <path>/api/agents/conversations/streamServer-Sent Events of the caller’s conversations: the list on connect, then each change and removal.
PATCH <path>/api/agents/conversations/{id}Rename a conversation, or move it to one of the caller’s folders.
PUT, DELETE <path>/api/agents/conversations/{id}/readMark a conversation read or unread for the caller.
POST <path>/api/agents/conversations/{id}/replyAdd the user’s message and have the agent reply. The reply runs on the server and is saved into the conversation as it arrives. 409 while another reply to the conversation is in progress.
GET, DELETE <path>/api/agents/conversations/{id}/replyFollow the reply in progress as Server-Sent Events from the pod writing it, or stop it from any pod.
POST <path>/api/workspace/validateValidates flow and resource files without running them.
GET, POST <path>/api/changesList and propose workspace changes.
GET <path>/api/changes/{id}One change with its diff.
POST <path>/api/changes/{id}/approve, /rejectDecide a pending change; a signed-in user only.
GET <path>/api/openapi.yamlThe spec itself.

Conversations from flows

With web.auth, each signed-in user sees their own conversations and the shared ones; without it, every conversation is shared.

A flow opens a conversation in the Agents tab by writing it to the system bucket (cache.system.db_name) with nats_kv_store. Under agents.conversations.<id> it is shared. Under agents.conversations.<user>.<id>, where <user> is the hex SHA-256 of the user id, only that user sees it; a flow started by an http_endpoint or mcp_tool with auth has the id in event.meta.auth.user_id, so a script gets <user> with sha256(event.meta.auth.user_id). The id uses only letters, digits, - and _. event.data.content is a Conversation from openapi.yaml as JSON, with origin: agent and unread: true, and optionally folder, e.g. Preflight/Klaviyo. It is unread for each user until they open it, and again when it changes. Marking a conversation read or unread, or moving it to another folder, affects only the user who does it; without web.auth it affects everyone.

- nats_kv_store:
    name: open_conversation
    url: "{{env.NATS_URL}}"
    bucket: flowgen_system
    operation: put
    key: "agents.conversations.{{event.meta.correlation_id}}"
    ttl: 30d

health

Kubernetes liveness/readiness listener.

FieldTypeDefaultDescription
enabledbooltrueEnable the listener.
portint8081Listening port.
EndpointResponse
GET /livez200 OK.
GET /readyz200 OK once startup has finished, 503 before and after a shutdown signal. A worker with no flows is ready.
GET /healthzAlias of /livez.

retry

Sets the default retry policy for every task. Individual tasks can override via their own retry field.

FieldTypeDefaultDescription
max_attemptsint / null10Maximum attempts before giving up. null for infinite retries.
initial_backoffduration1sDelay before first retry. Each subsequent retry doubles, with jitter.

See Retry for how events, task startup, subscribers and flow restarts retry.

telemetry

OpenTelemetry providers for metrics, traces, and logs. See Telemetry.

FieldTypeDefaultDescription
enabledboolrequiredSet true to initialize the provider.
backendobjectin-memoryBackend selection. Omit for the in-memory backend.
backend.typestring—memory or remote.
backend.endpointstring—Required for remote. gRPC endpoint of the collector.
backend.logs_per_flowusize1000Memory backend only. Log records retained per flow and level on each pod.
backend.metrics_per_flowusize1000Memory backend only. Metric samples retained per flow.
backend.portint8082Memory backend only. Port of the internal endpoint the other pods read logs and flow counters from in cluster mode.
service_namestringflowgenservice.name resource attribute.
metrics_export_intervalduration60sHow often metric snapshots are pushed. Ignored by the memory backend.

When telemetry is omitted entirely, no telemetry stack is started.

Running

flowgen --config /etc/flowgen/config.yaml

Flowgen validates the config at startup. Any field with a typo, missing required value, or invalid type produces an error before any flow runs.