Feature 6: Success Criteria and Actions
← Structural Results | Next: Execution Model →
UWS defines a first-class vocabulary for deciding whether an operation succeeded, and for specifying what to do when it did or did not. Criteria and actions are declared inline on the operation — there is no shared registry. This guide describes current UWS 1.12 semantics unless it identifies an earlier version.
Criterion Object
A Criterion describes a condition to evaluate:
| Field | Required | Description |
|---|---|---|
condition |
REQUIRED | For simple, a runtime expression or literal condition; otherwise a regex pattern, JSONPath query/pointer, or XPath 1.0 expression |
type |
optional | simple (default), regex, jsonpath, or xpath |
context |
REQUIRED when type is regex, jsonpath, or xpath |
Value to apply the condition against |
simple type (default) — uses the normative comparison grammar:
successCriteria:
- condition: $response.statusCode == 200
- condition: $response.body.success == true
- condition: $response.body.count > 0
regex type — evaluates context first, converts its value to a string or UTF-8 byte sequence, then searches it using RE2-compatible semantics. condition is the pattern:
successCriteria:
- condition: "^(200|201|204)$"
type: regex
context: $response.statusCode
jsonpath type — evaluates a JSONPath expression against a context value:
successCriteria:
- condition: "$.items[?(@.status == 'active')]"
type: jsonpath
context: $response.body
xpath type — evaluates an XPath expression against an XML context:
successCriteria:
- condition: "//order[@status='confirmed']"
type: xpath
context: $response.body
For jsonpath, context is a JSON value and condition is a JSONPath query
whose selected values are tested for truthiness, or a direct JSON Pointer
lookup. For xpath, context is XML text and condition is an XPath 1.0
expression. When any non-simple condition begins with the exact context
expression, UWS removes that prefix before interpreting the remaining pattern
or query. A missing pointer does not match; a present null value is found but
false. See the UWS 1.12 execution contract.
Failure Actions
onFailure handles a leaf runtime error or a false successCriteria result:
| Field | Required | Description |
|---|---|---|
name |
REQUIRED | Unique action name |
type |
REQUIRED | end, goto, or retry |
workflowId |
REQUIRED for goto (exactly one of workflowId/stepId) |
Target workflow |
stepId |
REQUIRED for goto (exactly one of workflowId/stepId) |
Target step |
retryAfter |
optional | Cancellable seconds to wait before retrying. MUST be ≥ 0 |
retryLimit |
REQUIRED for retry |
Number of retries after the initial attempt. MUST be ≥ 1 |
criteria |
optional | Additional conditions that scope this action |
When successCriteria is non-empty, every criterion must match; an empty list
adds no success condition. Within either action list, actions are considered in
declaration order and the first matching action is applied. All criteria on an
action must match, while an empty action criteria list matches immediately.
If criterion evaluation itself fails, the operation fails directly without an
action. If no failure action matches, the operation failure propagates. A retry limit
counts retries after the first attempt, so retryLimit: 3 permits four total
attempts; every retry gets a fresh operation timeout. An exhausted retry does
not fall through to a later action as a fallback.
Success Actions
onSuccess fires when the operation's successCriteria are all satisfied:
| Field | Required | Description |
|---|---|---|
name |
REQUIRED | Unique action name |
type |
REQUIRED | end or goto |
workflowId |
REQUIRED for goto (exactly one of workflowId/stepId) |
Target workflow |
stepId |
REQUIRED for goto (exactly one of workflowId/stepId) |
Target step |
criteria |
optional | Additional conditions that scope this action |
Example 1: Retry and Failure Routing
Retry server errors up to 3 additional times; end cleanly on client errors; route other matching failures to an error handler.
operationId: charge_payment
sourceDescription: stripe_api
openapiOperationId: createCharge
successCriteria:
- condition: $response.statusCode == 200
- condition: $response.body.status == "succeeded"
onFailure:
- name: retry_on_5xx
type: retry
retryAfter: 2
retryLimit: 3
criteria:
- condition: $response.statusCode >= 500
- name: skip_on_4xx
type: end
criteria:
- condition: $response.statusCode >= 400
- condition: $response.statusCode < 500
- name: escalate
type: goto
workflowId: payment_error_handler
retry_on_5xx only activates when status ≥ 500. The initial attempt plus three retries makes at most four attempts. If the retry limit is exhausted, that action returns the failure; skip_on_4xx and escalate are not fallback actions for the same 5xx failure. skip_on_4xx activates for 4xx. escalate catches failures that do not match either earlier action.
Example 2: goto to a Specific Step
On success, branch directly to a named step rather than a whole workflow:
operationId: check_eligibility
sourceDescription: rules_api
openapiOperationId: evaluateRules
successCriteria:
- condition: $response.body.eligible == true
onSuccess:
- name: fast_track
type: goto
stepId: approve_immediately
criteria:
- condition: $response.body.score >= 90
- name: standard
type: goto
stepId: queue_for_review
fast_track sends high-scoring applicants to approval; otherwise the unconditional standard action sends the applicant to the review queue. In UWS 1.11, goto is a terminal transfer: it unwinds the entire active top-level run, including any sub-workflow, loop, or forEach item, invokes the target and its dependencies in root orchestration scope, and ends the run after the target completes. It does not resume at the caller or continue with later siblings. A root target already completed, skipped, or in flight fails explicitly instead of replaying or waiting. A same-named record inside a prior workflow call does not count as a completed root target.
Example 3: goto to a Workflow on Failure
Route persistent failures to a dedicated error-handling workflow:
operationId: sync_inventory
sourceDescription: warehouse_api
openapiOperationId: syncStock
successCriteria:
- condition: $response.statusCode == 200
onFailure:
- name: retry_briefly
type: retry
retryAfter: 5
retryLimit: 2
criteria:
- condition: $response.statusCode >= 500
- name: raise_incident
type: goto
workflowId: ops_incident_workflow
For a failure that matches retry_briefly, the retry action is selected first. If its two retries are exhausted, the operation failure propagates; raise_incident does not automatically run as a fallback. Use mutually exclusive action criteria or handle retry exhaustion in the runtime/profile when the incident workflow must run afterward.
Example 4: end on Partial Success
Stop early when there is nothing left to do, without calling it an error:
operationId: list_pending_orders
sourceDescription: orders_api
openapiOperationId: getPendingOrders
successCriteria:
- condition: $response.statusCode == 200
onSuccess:
- name: nothing_to_process
type: end
criteria:
- condition: $response.body.total == 0
If the list is empty, execution ends cleanly. If items exist, the default behavior continues to the next step.
Example 5: Multi-Criterion successCriteria
All criteria in successCriteria must be satisfied for the operation to be considered successful:
operationId: create_session
sourceDescription: auth_api
openapiOperationId: initiateSession
successCriteria:
- condition: $response.statusCode == 201
- condition: $response.body.token != null
- condition: "^[A-Za-z0-9_-]{32,}$"
type: regex
context: $response.body.token
onFailure:
- name: auth_failure
type: goto
workflowId: handle_auth_error
All three criteria must pass. If the token is missing or malformed, auth_failure fires.
Inline Declaration
UWS does not provide a reusable component registry for criteria and actions. Each operation declares its own. This keeps the document self-describing and allows sibling operations to evolve independently without shared state.
From The Big Fixture
The large fixture combines simple and regex criteria with retry and goto failure actions:
onFailure "retry_run_llm_primary" {
type = "retry"
retryAfter = 5
retryLimit = 2
criterion {
condition = "$error.transient == true"
type = "simple"
}
criterion {
condition = "timeout|rate limit"
type = "regex"
context = "$error.message"
}
}
onFailure "review_run_llm_primary" {
type = "goto"
workflowId = "wf_manual_review"
criterion {
condition = "$error.transient != true"
type = "simple"
}
}
Full context: testdata/big/big.hcl.