Flow Configuration
Breyta configuration guide for wiring required connections, secrets, and runtime inputs.
Goal
Connect a flow definition to real runtime systems without hardcoding credentials.
Quick Answer
Declare dependencies in :requires, then configure required values before first run:
-
Set up reusable workspace connections first (recommended): Connections First
-
breyta flows configure <slug> ...for the default draft target -
breyta flows configure <slug> --target live ...orbreyta flows installations configure <installation-id> ...for live/installation targets -
breyta flows configure check <slug>(or--target live --version latest) to verify required values are present before running
Requires, Bindings, And Runtime Resolution
| Layer | Stored where | Example | Notes |
|---|---|---|---|
| Requirement contract | Flow definition :requires | {:slot :orders-api :type :http-api ...} | Declares what must be provided. |
| Bound value | Target profile bindings (draft / live / installation) | orders-api -> conn-123 | Set by flows configure / installation config. |
| Runtime resolution | Selected run target | flows run <slug> or --target live | Run uses resolved profile bindings for that target. |
requires does not store connection ids directly.
Connection ids are stored in target profile bindings and resolved at run time.
Display icon
Flow icon rendering can also be curated independently of runtime bindings.
primaryDisplayConnectionSlotis display-only metadata used anywhere the flow icon is rendered today.- In the UI this is shown as the flow's primary integration.
- Set it from the flow detail UI or with
breyta flows update <slug> --primary-display-connection-slot <selector>. - Clear it with
breyta flows update <slug> --primary-display-connection-slot ''. - Valid selectors come from declared
:requiresslots or normalized keys exposed by:connections. - If the saved selector is unset, missing, or no longer matches the flow, rendering falls back to the first explicit
:connectionsicon, then the first inferred:requiresicon. - This metadata is workspace-scoped authoring state, not part of the pulled flow source file.
Use breyta flows show <slug> --pretty to inspect the current primaryDisplayConnectionSlot.
When a flow exposes connection metadata, JSON responses also include _hints with the canonical inspect/set/clear commands.
Activation Inputs Behavior
Activation inputs are target-scoped setup values saved on the selected runtime target and reused on later runs until they are changed.
- Configure draft target inputs with
breyta flows configure <slug> --set activation.<key>=<value>. - Configure installation inputs with
breyta flows installations configure <installation-id> --input '{"<key>":"<value>"}'. - At run time, these saved setup values are applied automatically for that target.
Important lifecycle rule:
breyta flows release <slug>andbreyta flows promote <slug>may advance live and track-latest installations, but they do not clear previously configured activation inputs.- Update activation inputs explicitly through configure commands when you need to change them.
Blob-storage slots add a required setup root
Installer-owned :blob-storage slots always add a required setup control for the storage root.
Flow authors can customize its default/label/description through the slot config, but cannot disable it.
Example:
{:requires [{:slot :archive
:type :blob-storage
:label "Archive storage"
:config {:prefix {:label "Folder prefix"
:description "Stored under this folder in the selected storage connection."
:default "reports"
:placeholder "reports/customer-a"}}}]}
At setup time the installer binds the slot to a concrete storage connection.
For end-user installations, the authored prefix becomes a private effective root such as installations/<profile-id>/reports until the installer explicitly saves another root in bindings.<slot>.config.root.
Persisted blob writes and runtime resource pickers both reuse that same effective root automatically when they point at :slot :archive.
For platform storage, connected persists write under workspaces/<ws>/storage/<root>/....
They do not add any hidden persist/<flow>/<step>/<uuid> segments under that root.
What the installer configures:
- bind the slot itself to a blob-storage connection such as
platform - optionally choose an explicit storage root override when this installation should use a shared lane instead of its default private lane
For an end-user installation with profile prof-1, the default effective root for the example above is:
installations/prof-1/reports
That means a later step like:
:persist {:type :blob
:slot :archive
:path "exports/{{input.customer-id}}"
:filename "summary.pdf"}
writes to:
workspaces/<ws>/storage/installations/prof-1/reports/exports/<customer-id>/summary.pdf
If the installer later saves an explicit shared root such as reports/customer-a, that saved root overrides the private default.
The flow author never repeats the root in the step itself. The installer owns that root once, and both persistence and runtime pickers reuse it automatically. That full path is the final object path for the connected write.
Blob-storage sharing is installer-configured
Blob storage is defined by local :requires slots, and sharing happens only when installers point different flows at the same concrete storage location.
By default, two end-user installations stay isolated because each one derives its own private root, such as installations/<producer-profile-id>/reports and installations/<consumer-profile-id>/reports.
Producer flow:
{:requires [{:slot :archive
:type :blob-storage
:label "Archive storage"
:config {:prefix {:default "reports"}}}]}
Consumer flow:
{:requires [{:slot :archive
:type :blob-storage
:label "Archive storage"
:prefers [{:flow :daily-report-export
:slot :archive}]
:config {:prefix {:default "reports"}}}
{:kind :form
:collect :run
:fields [{:key :report
:field-type :resource
:slot :archive}]}]}
Installer setup decides whether those two flows share only when both installations explicitly converge on the same root:
{:bindings {:archive {:binding-type :connection
:connection-id "platform"
:config {:root "reports/customer-a"}}}}
If both installations use the same backend and the same root, the consumer picker sees the producer's persisted files. If they use different roots, they stay isolated.
:prefers is only an intent hint. It does not create a hard runtime binding, and it does not replace the installation-scoped default root.
To share, the installer must still explicitly save the same connection + root on both flows.
Notes:
- blob-storage intent lives in local
:requires, not in top-level:connections :persist :slotalways points at a local blob-storage slot such as:archive- runtime resource pickers reuse the resolved
connection + rootbehind that local slot - end-user installations derive a private default root from the authored prefix; shared roots require an explicit override
- local slot names are just author intent; the installer-controlled storage root is the actual sharing boundary
- two flows may use different local slot names and still share if installers point them at the same concrete location
Form Input Contract
Use :requires with {:kind :form ...} when the flow needs structured user input.
| Contract piece | Meaning |
|---|---|
:collect :setup | Enter once during setup and reuse on later runs until changed |
:collect :run | Ask for the value each time the flow is run |
:provided-by :installer | End user must supply the value |
:provided-by :author | Flow author supplies it; the installer is not prompted |
Supported field types:
:field-type | Expected UI |
|---|---|
:text | Single-line text input |
:textarea | Multi-line text input |
:select | Dropdown |
:boolean | Checkbox |
:number | Numeric input |
:date | Date picker |
:time | Time picker |
:datetime | Date-time picker |
:resource | Resource picker |
Resource field expectations:
:field-type :resourceis currently run-only, so use it with:collect :run.- The UI renders the picker only when the field is declared in
:requires. - The flow receives resource references, not file contents.
- The default resource picker filter is
:file, which covers uploads and persisted blobs. :acceptsupports exact MIME types like"application/pdf"and wildcard prefixes like"text/*".- Add
:resource-typesonly when you need something other than the default file picker, then combine it with:acceptif needed. :slot <slot>scopes the picker to a local blob-storage slot declared in:requires.- when that slot is bound to platform storage, the picker scopes to the configured
connection + root. - runtime pickers share artifacts when installers configure different flows to the same concrete storage location.
:sourcestill exists as a legacy/internal picker-routing override, but normal authored flows should omit it.
Example:
{:requires [{:kind :form
:collect :setup
:fields [{:key :region
:label "Region"
:field-type :select
:required true
:options ["EU" "US"]}]}
{:kind :form
:collect :run
:fields [{:key :question
:label "Question"
:field-type :text
:required true}
{:key :resources
:label "Resources"
:field-type :resource
:slot :archive
:required true
:multiple true
:accept ["application/pdf" "text/plain"]}]}]}
For text-oriented flows, prefer a MIME allowlist like:
{:key :resources
:label "Text resources"
:field-type :resource
:required true
:multiple true
:accept ["text/*"
"application/json"
"application/xml"
"application/edn"]}
Inside the flow:
'(let [input (flow/input)]
{:region (:region input)
:question (:question input)
:resources (:resources input)})
Example runtime input:
{:region "EU"
:question "Summarize the attached reports"
:resources [{:type :resource-ref
:uri "res://v1/ws/ws-1/file/report-a"}
{:type :resource-ref
:uri "res://v1/ws/ws-1/result/summary-b"}]}
Binding-Based Usage And Delete Guards
breyta connections usages and breyta secrets usages report where bindings currently reference each id.
Delete commands are guarded by those binding references:
breyta connections delete <connection-id>breyta secrets delete <secret-id>
If any draft/live/installation profile still references the id, delete is blocked until wiring is moved/unset.
1. Declare Requires In The Flow
{:requires [{:slot :orders-api
:type :http-api
:label "Orders API"
:base-url "https://api.example.com"
:auth {:type :bearer}}
{:slot :risk-api-key
:type :secret
:label "Risk API Key"}]}
requires declare what must be configured before successful runtime execution.
OAuth-capable HTTP API slots
When a flow requires a delegated OAuth account, declare the slot as an :http-api requirement with an :oauth contract.
Example:
{:requires [{:slot :x
:type :http-api
:label "X account"
:base-url "https://api.x.com/2"
:oauth {:provider :any
:scopes {:required ["tweet.write"
"tweet.read"
"users.read"]
:any-of ["offline.access"]}
:requires-refresh-token true}}]}
What this changes in setup:
- the setup sidepeek can prefill the OAuth connect flow with required scopes and base URL hints
- users can either reuse a compatible existing OAuth connection or create a new one from the same setup surface
- the callback returns to the flow setup screen and preselects the new connection for the slot
- OAuth setup return paths must stay inside the workspace; external
return-urlvalues are rejected
For custom OAuth connections, the slot contract still matters even when the provider is :any: setup uses the slot's scopes/base URL to guide the connect flow and reconnect path.
What end users still need from provider docs:
- authorize URL
- token URL
- callback/redirect URI registration
- probe URL for health checks
- client ID and client secret
- any required extra authorize params
- whether PKCE must be enabled
Recommended author/operator guidance for custom OAuth slots:
- keep
:labelspecific, for exampleX accountinstead ofOAuth - include the expected API
:base-urlin the slot requirement when it is known - keep
:oauth.scopes.requiredas small as possible - include
:oauth.requires-refresh-token truewhen the flow expects long-lived reuse
Example operator handoff note for a custom OAuth slot:
Configure an OAuth app at the provider and register:
https://<your-breyta-base-url>/<workspace-id>/api/oauth/callback
Then create an OAuth account connection in Breyta with:
- Base URL: https://api.x.com/2
- Authorize URL: https://x.com/i/oauth2/authorize
- Token URL: https://api.x.com/2/oauth2/token
- Probe URL: https://api.x.com/2/users/me
- Scopes: tweet.write tweet.read users.read offline.access
- PKCE: enabled
Current product limitation:
- The custom OAuth connection form currently expects both a client ID and client secret, so public-client-only provider setups are not yet supported through this path.
2. Configuration Targets
draft and live resolve the same flow definition schema through different runtime targets:
| Aspect | Workspace draft | Installation live / explicit installation |
|---|---|---|
| Definition used at runtime | Latest pushed working copy | Installed released version |
| Configure command | breyta flows configure <slug> --set ... | breyta flows configure <slug> --target live --version <n|latest> --set ... or breyta flows installations configure <installation-id> --input ... |
| Check command | breyta flows configure check <slug> | breyta flows configure check <slug> --target live --version <n|latest> (or inspect/install specific target separately) |
| Webhook endpoint behavior | Development/staging-oriented target; endpoint values may change during iteration | Installed runtime target; endpoint values are installation/live specific and can differ from draft |
| Run command | breyta flows run <slug> --wait | breyta flows run <slug> --target live --wait or --installation-id <id> |
| Primary use | Build/test iteration | Stable installed behavior |
| Context | What it controls | Canonical commands guide |
|---|---|---|
| Workspace default target | Values used by breyta flows run <slug> with no explicit installation target | CLI Workflow |
Installation target (live or explicit installation id) | Installation-specific values and trigger/webhook behavior | Installations |
Use connection reuse as a default operating rule (breyta connections list before creating new connections).
When validating installation-target configuration, run with explicit installation targeting:
breyta flows run <slug> --target live --wait- or
breyta flows run <slug> --installation-id <installation-id> --wait
Live version pinning and slot migrations
When a new release adds or renames required slots, pin configuration to the version you plan to run:
breyta flows configure check <slug> --target live --version <n|latest>breyta flows configure <slug> --target live --version <n|latest> --set <slot>.conn=<connection-id>breyta flows configure <slug> --target live --from-draft --version <n|latest>
This avoids validating live updates against stale profile-version requirements.
For a brand-new flow with required slots, use the same version-pinned live commands before the first release. Without --version latest, live checks can miss unresolved slots and flows release <slug> can fail with live_config_incomplete.
3. Secret Handling Rules
- never hardcode secrets in flow files
- keep secret values in bindings/profile input
- rotate by updating bindings, then re-running
- keep placeholder semantics in generated templates (
:redacted,:generate)
For secret lifecycle commands and examples, use Secrets.
Frequently Asked Questions
What is the difference between workspace configuration and installation configuration?
In the canonical surface, use draft target configuration (flows configure) and installation configuration (flows installations configure).
Legacy bindings commands are compatibility behavior.
How do I rotate a secret without changing flow code?
Update the configured secret value for the target you run against, then re-run the flow (see Secrets).
Common Pitfalls
- running before required config is applied
- using configuration commands after a failed push
- mixing workspace-target config with installation-target config
- creating duplicate connections during iteration instead of reusing existing ones