Apps
Publish a persistent form that calls an exact workflow destination and displays its execution result.
Apps give workspace members and authorized external visitors a persistent interface over a published workflow. The first version supports one page with a form, a processing button and a result panel. The backend can call a child workflow through the existing workflow runtime. The App itself contains no executable HTML, JavaScript or custom URLs.
Create and publish an App
- Publish and enable a workflow destination. Its saved version must have one Manual Trigger with between one and twenty declared text, number or boolean inputs.
- Open Apps in the workspace navigation and choose Create App.
- Enter the App name and page title, then choose the published workflow and destination. The editor builds the visible inputs from that workflow's declared contract. Change the field labels and button and result labels as needed. The live preview updates as you edit; it neither saves nor executes requests.
- Choose Save draft. Open Preview saved draft to inspect the exact form that publication will use. Unsaved changes must be saved before the publication review can be acknowledged.
- Review the saved form and its workflow and destination names, acknowledge that review, and choose Publish App. Internal binding identifiers remain available in Technical details. If the saved backend is no longer among the eligible destinations loaded in the editor, review the workflow before publishing.
- Open the published App, fill its fields and choose its processing button. Follow the execution state and open the execution reference for its recorded details.
Saving a draft leaves the published App unchanged. Publishing creates a new immutable version; a viewer that still has an older version must reload before starting another request. Apps use the workspace's shared palette and its normal canonical workspace route.
The App list and workflow destination selector load in pages. Choose Load more Apps or Load more workflows to continue. A workflow page can contain no eligible destinations while still offering more; unsupported trigger contracts do not hide older eligible workflows.
Bindings and permissions
Each published version records the workflow, execution destination, workflow version, activation and input contract. The server resolves that binding from the published snapshot. An invocation accepts the App version, button identity and form values; it cannot choose another workflow or destination. Every declared backend input has a visible form field. Extra fields, unsupported types and duplicate field names are refused.
Workspace read authority permits viewing Apps. Workspace write authority permits creating, saving and publishing them. Processing requires workspace execution authority. A Viewer cannot process a form, and organization oversight does not confer editing or execution authority.
Internal requests run with the current authenticated member or principal, subject to the existing workflow, destination and AIMS checks. Publishing an App does not delegate the publisher's authority to its viewers. Stopping or replacing the bound activation, changing its version or losing authorization prevents the invocation; the App never silently switches to another backend. Review the destination and publish the App again after an intentional backend change.
Child workflows use the parent destination's approved dependency bindings: each records the child's workflow, destination and immutable activation, which identifies its version. A newer child draft does not replace that backend. Apps recheck the pinned dependencies and their AIMS authority before accepting a request or replay, and the runtime checks them again at dispatch. Revoking a child's approval prevents execution; the App does not fall back to another child version.
Share with external visitors
After publishing, open the App editor's External access panel. Choose a mode and Save access:
| Mode | Who can open the link |
|---|---|
| Internal | Workspace members only; no external link |
| Public link | Anyone with the link |
| Access key | Anyone with the link and the shared key |
| Username and password | Anyone with the link and the configured username/password pair |
The protected modes use one shared credential per App, rather than separate external accounts. A new key or password must have at least eight characters. The saved password is never displayed. Leaving it blank retains it only when the mode and username stay the same. Copy the link after saving. Rotate share link replaces it; switching back to Internal disables it. Saving access expires existing visitor sessions, including when you change the password or rotate the link. Password guessing is limited across networks as well as by source network. After ten failed attempts, access may be paused for up to 15 minutes, including for a correct password. An authorized editor can save the access settings again to start a new credential revision and clear that shared attempt budget.
Enabling external access requires both workspace write and execution authority. The grant records the member who saved it and the exact published App version. External requests run using that member's current execution authority, scoped to this App's approved workflow and pinned child dependencies. Membership, execution permission and AIMS remain mandatory. Disabling or changing the grant is checked again at worker admission and before each node. A newer App publication pauses external access until an authorized member reviews and saves the access settings for that publication. When a timer wait becomes due, a permanent authority denial ends the parked run as failed; a stopped execution target cancels it. Temporary infrastructure failures remain eligible for another resume attempt.
Visitors do not join the workspace. Each browser gets its own session, lasting up to 24 hours, even when several people use the same password. Its history and results belong to that session; another browser cannot read them by copying a request URL. Signing out or losing the session means that browser must authenticate again, with a new session and a separate history. External requests do not appear in the grantor's personal internal history.
Visitors see the published form and their own redacted results, without workspace navigation, execution links, draft bindings or logs. An App grant does not establish a customer Product identity or customer consent. Use Product MCP for those operations.
Use the workflow's final field mapping and Output step to choose the business values visitors should receive. The server removes workflow-call bookkeeping, internal node configuration and execution references from native runtime results before returning them through the visitor page or MCP. The result still contains the returned values and validation outcomes. The visitor page has no technical JSON disclosure. Signing out clears its private view, form and selected request URL. A failed current-version reload preserves what the visitor typed; an unavailable link hides the cached form. Submissions stay disabled while sign-out is in progress. Opening a request from the visitor history adds a browser history entry, so Back and Forward revisit requests without sending them again. If Output directly returns the trigger's data, the visitor sees those values without the trigger's technical summary. Handled node errors and failed API Batch items show a generic failure message. Provider diagnostics, internal node names, request URLs and response headers from those runtime records remain private; successful batch response bodies remain available as returned business data.
Execution and recovery
Submitting starts the existing workflow runtime and returns an invocation reference (and a run reference for internal members). The App shows pending, running, waiting, waiting for approval, error or succeeded according to the backend. An accepted request is not a claim that processing succeeded. Internal results belong to the member who submitted the request; external results belong to the submitting visitor session.
The result panel displays the persisted output of that execution. It never reads the latest run of a workflow. Text, named values, lists and compatible tables are displayed directly; the complete structured output remains available in the collapsed Technical details. Only the execution status is announced as a live update, rather than the entire output.
Recognized native workflow envelopes show returned field values and validation outcomes directly. For example, the request-intake sample displays the child's normalized values and validation, rather than presenting the workflow-call input as its result. Runtime bookkeeping stays in technical details. Unknown output shapes keep the generic view and their complete structured data.
After a request is accepted, the App's URL includes its invocation identifier in request. Reloading
that URL reads the persisted result and does not invoke the workflow again. My requests lists your
own requests, newest first, with creation times in UTC; choose Load more requests for older entries.
Opening a history entry does not rerun it. Links do not grant access to another member's results.
If a selected request used an older published App version, the viewer identifies it as an older result.
Choose New request to clear the selected result and start an empty form.
Fields are validated before submission and errors identify the affected input. Processing… appears while the request is being accepted or processed. The previous result and execution link stay hidden while a new request is being accepted. If the App was republished, Load current version explicitly loads the newer form and clears its inputs after a successful reload. A failed reload preserves them. A changed workflow backend must instead be reviewed and republished by an editor.
Text inputs accept up to 4,000 characters; numeric inputs must be finite and within −1,000,000,000 to 1,000,000,000. App results are limited to 100,000 serialized UTF-8 bytes. A larger result keeps the execution's actual status and link, but omits its payload; open the execution to inspect it. Secret patterns are redacted from result text, structured values and execution error messages before they reach the App. This does not remove the caller's ownership and authorization requirements.
If the send response is lost, fails unexpectedly or reports that the same request is still being accepted, normal submission stays disabled. Retry same request uses the retained values and key to recover the original receipt instead of creating a second request. A history refresh alone does not unlock the form.
Retry same request reuses the original idempotency key and arguments. A completed receipt replays without creating another run while retained; using that key with different values is a conflict. When queue admission fails before attempting to persist an execution, the server removes that attempt from history and permits the same key and arguments to be retried after verifying cleanup. A preparation failure before the queue is called also permits a retry. If the request's own save reply was lost, an entry may remain in history without a workflow execution; that entry alone does not mean the workflow ran. Once execution persistence is attempted, a missing execution row cannot prove that the request did not start, so the key remains quarantined. Permission and backend checks still apply. If the server reports an uncertain outcome, the key remains quarantined and Retry same request is hidden. Choose Refresh my requests to look for the accepted request and ask an administrator before sending it again. The form remains disabled for this uncertain attempt. An empty or failed history refresh is not proof that the request did not start.
If an execution status read fails, the form stays disabled. Use Retry to read its status again, or choose New request to start with an empty form. Selecting a request in the history also clears unsent form values, including when you select the request that is already open.
After five minutes without an available execution, measured using the database clock, automatic polling stops and the App reports that the execution is unavailable. It may still be starting, or its record may have been removed; this state does not prove that nothing ran. Check request status performs a read-only refresh. There is no execution link until a run is available. Reading this state never deletes the request, releases its key or restarts it. A real queued execution retains its pending state.
Apps require PostgreSQL. A storage failure is reported as a failure; it does not return a cached App or switch to an in-memory deployment. A concurrent editor's change produces a revision conflict. Your unsaved fields remain visible when a newer revision is fetched. Choose Reload saved draft to replace those fields explicitly, then review the current draft before saving or publishing again.
A successful save or publication remains successful if the subsequent read fails. The editor keeps its accepted revision and shows a refresh warning, rather than a conflict with its own save. Choose Reload App to fetch the persisted version. Publication review remains unavailable until that saved version has been loaded successfully.
Invocation receipts store identifiers and a creation time, without copying form values or execution output. They have no age-based expiry and are removed with their workspace or the erasure of an associated end-user run. End-user exports include that run rather than a duplicate App receipt. Shared drafts and published App versions remain workspace configuration.
Request intake sample
The local sample Recepción de solicitudes has Solicitud, Cantidad and Prioridad fields. Its Procesar solicitudes workflow calls Validar y normalizar solicitud, which projects the declared fields to their types and checks their presence. The parent returns the child result and its execution reference. Different inputs are processed by the workflow; the frontend does not manufacture sample results. This sample requires no paid AI service, OAuth account, email or external effect.
The repository provides scripts/create-request-intake-app.ts for an existing synthetic workspace in
a disposable local fluxus_test database. It requires separate authorized owner and AIMS reviewer
accounts and an explicit --confirm. It creates the workflows through canonical capabilities, performs
the normal governance review and destination publication, then creates and publishes the App. It refuses
a nonlocal or non-test database. It is not a production seed.
The script returns the App and workflow identities plus the canonical workspace path for opening it.
The fixture-specific browser journey is tests/e2e/apps.spec.ts; it requires a local Docker origin,
the returned fixture JSON through CONFLUYE_APPS_FIXTURE and normal synthetic account credentials. It
is skipped in browser runs that have not provisioned this fixture. PostgreSQL/runtime and both MCP
scopes are exercised separately by tests/integration/apps-prisma.test.ts.
MCP
Workspace and organization MCP expose the same App operations as the UI:
| Tool | Purpose |
|---|---|
apps.list | List workspace Apps |
apps.get | Read one App's draft and published snapshot |
apps.backends | List enabled published destinations with supported input contracts |
apps.create | Create a validated draft |
apps.save | Save a draft using its expected revision |
apps.publish | Publish an immutable version using its expected revision |
apps.invoke | Process one published button with validated form values |
apps.run | Read the caller's invocation state and result |
apps.invocations | List the caller's request identifiers and creation times for one App |
Mutations require confirm: true and an idempotencyKey. On the organization endpoint each call also
requires an explicitly selected, authorized workspaceId. At the workspace endpoint its URL supplies
that workspace; do not add a workspace override to the arguments. See the
MCP reference for request shapes.
apps.run returns workflow output and requires workspace secret-read or write authority as well as
ownership of the invocation. apps.invocations uses the same restricted MCP authority, returning
identifiers and creation times without inputs or outputs. An organization OAuth grant limited to read
cannot discover or call these tools, even when the account behind that grant is a workspace Owner.
