Private Preview
Business Workflows and Apps
You can author Business Workflows in the semantic layer, including writable tables, stored procedures and virtual procedures. An App runs one by name with app.runDataAction().
Before you begin
Section titled “Before you begin”runDataAction() works in astrato dev and in a published App when all of these hold:
- Business Workflows are enabled for the tenant, and the workflow is registered and published in the semantic layer.
- Signed-in viewers have access to the semantic layer, and data-action access is turned on for your Astrato environment. This is not a setting on the layer or the publication: Astrato turns it on, and a viewer’s sign-in then carries the
data:actionpermission. Without it, a run is refused withforbidden. See Run Business Workflows from a Data App. - The data connection has an explicit, non-legacy Deployment Region, the same one a Data App needs to load data. See Choose a Deployment Region.
- Your App uses
@astrato-io/app-runtime0.5.0 or later, the first version withrunDataAction(). - For
astrato dev, use@astrato-io/cli0.1.3 or later. Its login includes thedata:actionscope, so runastrato loginagain after upgrading; a login saved by an earlier version lacks it.
Public publications cannot run workflows: an anonymous viewer is refused. Preview connections in the Astrato editor are read-only.
Workflow discovery and execution-status lookup are not supported by the App runtime or CLI.
To register a workflow first, see Business Workflows. For the whole route from a mapped table to a saved row, follow Let users edit data and save it back.
Run a workflow
Section titled “Run a workflow”The App names a registered workflow and passes one object of named arguments per run. It cannot name a data source, schema or SQL.
const result = await app.runDataAction({ name: 'Resolve comment', arguments: [{ commentId: 'c-102', resolution: 'Fixed' }],});Omit arguments to run a workflow without parameters once. A single call takes up to 1,000 argument objects.
Arguments for a writable table
Section titled “Arguments for a writable table”For a writable table, each key field and writable field is an argument named after the field in the semantic layer, not the warehouse column. An Update rows workflow named Update order status, with key field order_id and writable field status, takes:
await app.runDataAction({ name: 'Update order status', arguments: [{ order_id: 1042, status: 'Shipped' }],});Send every key field, including each one of a composite key, and every writable field. A field whose value the workflow takes from a user attribute is filled by Astrato and is not sent. Anything else is refused with unknown_parameter. A workflow allows one operation, so a screen that edits and adds rows runs an Update rows workflow and a separate Insert rows workflow.
The workflow that runs is the published version of the semantic layer. A saved draft or an unpublished edit is never run, so publish the layer after changing a workflow. A published App is pinned to the layer revision it was published against. If the layer has been published again since, the call is refused with app_unavailable; republish the App.
Astrato runs the workflow as the signed-in viewer, so the viewer’s own permissions on the layer and the data source apply. The App has no confirmation step of its own from Astrato: ask the viewer in your interface before you call runDataAction().
When a step belongs to another role, for example a checker approving a maker’s change, say next to
the control which role can take it. A disabled button with no reason reads as a fault. Keep the
role check in the workflow itself, so it protects the data when the interface is wrong, and take the
viewer’s identity from the signed-in user rather than from an App argument; see
Restrict a write to the signed-in user.
A refusal raised by the workflow’s own SQL arrives as a failed outcome, not as
data_action_forbidden; see Return an error message.
Read the result
Section titled “Read the result”runDataAction() resolves with one outcome per argument object:
{ "dataAction": "Resolve comment", "invocations": 1, "outcomes": [{ "inputIndexes": [0], "status": "ok", "effects": "committed" }]}status is ok, failed, notExecuted or unknown. effects is none, committed, rolledBack or unknown. A large result keeps each outcome’s status and effects and drops its rows, and resultTruncated is true.
A failed outcome carries an error with a code and, when the engine names it, the statement that failed. When the workflow’s own SQL wrote the message, error.authored is true and error.message is that message, with the code DATA_ACTION_AUTHORED_ERROR. Show that message as written. A failure the workflow reports with its own application code can also carry a message while authored is absent: it is the workflow’s text too, and an App may show it, while Astrato’s own screens distinguish authored messages from engine guidance. Recognized engine failures carry authored: false, a safe message, and, when available, class and retryable. These messages explain missing permissions or objects, procedure signature mismatches, invalid error tags and reserved markers in inputs. Unclassified failures have no displayable message. Raw warehouse diagnostics, SQL and rejected input values are not guidance.
DATA_ACTION_AUTHORED_ERROR_INVALID carries safe guidance explaining why an authored tag was refused, without a failure class. It is engine guidance, not the rejected tag content.
{ "inputIndexes": [0], "status": "failed", "effects": "rolledBack", "error": { "code": "DATA_ACTION_AUTHORED_ERROR", "message": "Budget locked.", "authored": true, "statement": "check" }}Render error.message as plain text, never HTML. Existing Apps filtering for authored: true must update their error handling and be republished to show engine guidance. retryable does not authorize an automatic retry.
Astrato shows the viewer nothing when a workflow runs: the App’s own interface reports the outcome. Show a confirmation when every outcome is ok, show error.message when an outcome is failed and has one, offer to rerun only the arguments at the failed outcome’s inputIndexes when its error.retryable is true, show a generic message when it has no message, show the error message when the call rejects, and run affected queries again after a committed change, because existing results do not refresh by themselves. If any outcome has unknown effects, check the data before running the workflow again.
Recognised warehouse failures
Section titled “Recognised warehouse failures”The engine classifies common warehouse errors and returns a fixed message, never the warehouse’s own text. The message identifies the permission, object or parameter when the engine can establish it safely; raw SQL and rejected input values remain private.
class |
error.code |
retryable |
|---|---|---|
procedureSignatureMismatch |
DATA_ACTION_PROCEDURE_SIGNATURE_MISMATCH |
false |
invalidInput |
DATA_ACTION_RESERVED_ERROR_MARKER |
false |
invalidInput |
DATA_ACTION_INVALID_INPUT |
false |
objectNotFound |
DATA_ACTION_OBJECT_NOT_FOUND |
false |
constraintViolated |
DATA_ACTION_CONSTRAINT_VIOLATED |
false |
permissionDenied |
DATA_ACTION_PERMISSION_DENIED |
false |
timeoutOrLimit |
DATA_ACTION_TIMEOUT_OR_LIMIT |
true if effects is none or rolledBack |
concurrencyConflict |
DATA_ACTION_CONCURRENCY_CONFLICT |
true if effects is none or rolledBack |
connectionUnavailable |
DATA_ACTION_CONNECTION_UNAVAILABLE |
true if effects is none or rolledBack |
{ "inputIndexes": [0], "status": "failed", "effects": "none", "error": { "code": "DATA_ACTION_INVALID_INPUT", "message": "The warehouse rejected an input value. Check its type and allowed range before trying again.", "authored": false, "class": "invalidInput", "retryable": false }}Classification coverage depends on the warehouse and failure. Unrecognised warehouse failures keep the generic EXECUTION_FAILED code. Engine validation refusals, such as invalid authored tags and reserved markers in inputs, are separate from warehouse-error classification. In Astrato’s own screens, an input form in Nash shows this message and adds “Nothing was changed, so it is safe to run it again” only when retryable is true. For a classified failure that is not retryable, the thread summary advises not to run it again unchanged.
Handle errors
Section titled “Handle errors”A refusal rejects with an AstratoAppRuntimeError whose response.error carries the code and a short message. The workflow’s own message reaches the App, for example OVERRIDE_REQUIRED: a manager must approve. Warehouse diagnostics and SQL do not. To return a message from a SQL statement, see Return an error message from a Business Workflow.
| Code | Meaning |
|---|---|
data_actions_disabled |
Business Workflows are not enabled for the tenant. |
data_action_not_found |
The published layer has no workflow with that name. |
invalid_arguments, unknown_parameter, missing_parameter |
The arguments do not match the workflow’s declared parameters. |
invalid_parameter_value |
A value cannot be held by its parameter’s type, for example text for an integer or a number for a string. Only a decimal parameter takes decimal text. |
data_action_refused |
The workflow or the data source refused the run. |
exit_node_required |
The data connection has no exit node that can run workflows. |
data_action_execution_unavailable |
The data connection cannot execute workflows. |
data_action_connection_unsupported |
The connection’s driver cannot run workflows. Use a supported connection. |
data_action_connection_inaccessible |
The caller cannot access the connection the layer uses. |
forbidden |
The viewer cannot open the App or its data, or the sign-in lacks data:action. |
data_action_forbidden |
The viewer is not allowed to run it. |
data_action_anonymous_refused |
The viewer is anonymous on a public publication. |
data_action_scope_required |
The astrato dev sign-in lacks data:action. Sign in again. |
data_action_outcome_unknown |
The answer was lost; the write may have run. |
Never run a workflow again after data_action_outcome_unknown, after a rejection whose message says the outcome is unknown, or after an outcome with effects: "unknown". Check the data first. Astrato does not retry a run for you.
Existing explicit table-write and stored-procedure APIs are documented separately in Writeback and SQL. They do not execute semantic Business Workflow definitions or enforce their authored settings, and they remain unavailable from a published App.
What next
Section titled “What next”- Let users edit data and save it back: a worked example, from a mapped table to a saved row.
- Create a writable table workflow: key fields, writable fields and operations.
- Manage Data Apps: what a Data App needs before it can run a workflow.