Operate

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 ... or breyta 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

LayerStored whereExampleNotes
Requirement contractFlow definition :requires{:slot :orders-api :type :http-api ...}Declares what must be provided.
Bound valueTarget profile bindings (draft / live / installation)orders-api -> conn-123Set by flows configure / installation config.
Runtime resolutionSelected run targetflows run <slug> or --target liveRun 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.

  • primaryDisplayConnectionSlot is 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 :requires slots 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 :connections icon, then the first inferred :requires icon.
  • 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> and breyta 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 :slot always points at a local blob-storage slot such as :archive
  • runtime resource pickers reuse the resolved connection + root behind 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 pieceMeaning
:collect :setupEnter once during setup and reuse on later runs until changed
:collect :runAsk for the value each time the flow is run
:provided-by :installerEnd user must supply the value
:provided-by :authorFlow author supplies it; the installer is not prompted

Supported field types:

:field-typeExpected UI
:textSingle-line text input
:textareaMulti-line text input
:selectDropdown
:booleanCheckbox
:numberNumeric input
:dateDate picker
:timeTime picker
:datetimeDate-time picker
:resourceResource picker

Resource field expectations:

  • :field-type :resource is 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.
  • :accept supports exact MIME types like "application/pdf" and wildcard prefixes like "text/*".
  • Add :resource-types only when you need something other than the default file picker, then combine it with :accept if 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.
  • :source still 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-url values 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 :label specific, for example X account instead of OAuth
  • include the expected API :base-url in the slot requirement when it is known
  • keep :oauth.scopes.required as small as possible
  • include :oauth.requires-refresh-token true when 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:

AspectWorkspace draftInstallation live / explicit installation
Definition used at runtimeLatest pushed working copyInstalled released version
Configure commandbreyta flows configure <slug> --set ...breyta flows configure <slug> --target live --version <n|latest> --set ... or breyta flows installations configure <installation-id> --input ...
Check commandbreyta flows configure check <slug>breyta flows configure check <slug> --target live --version <n|latest> (or inspect/install specific target separately)
Webhook endpoint behaviorDevelopment/staging-oriented target; endpoint values may change during iterationInstalled runtime target; endpoint values are installation/live specific and can differ from draft
Run commandbreyta flows run <slug> --waitbreyta flows run <slug> --target live --wait or --installation-id <id>
Primary useBuild/test iterationStable installed behavior
ContextWhat it controlsCanonical commands guide
Workspace default targetValues used by breyta flows run <slug> with no explicit installation targetCLI Workflow
Installation target (live or explicit installation id)Installation-specific values and trigger/webhook behaviorInstallations

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

Related

As of Mar 27, 2026