workflows — state machine definitions
cyoda-go version 0.6.2
workflows
Section titled “workflows”workflows — workflow state machine definitions: states, transitions, processors, and criteria.
SYNOPSIS
Section titled “SYNOPSIS”POST /api/model/{entityName}/{modelVersion}/workflow/importGET /api/model/{entityName}/{modelVersion}/workflow/exportContext path prefix is CYODA_CONTEXT_PATH (default /api). All endpoints require Authorization: Bearer <token> except when CYODA_IAM_MODE=mock.
DESCRIPTION
Section titled “DESCRIPTION”A workflow definition is a named finite state machine attached to an entity model. Workflows are stored per model reference (entityName, modelVersion). A model may have multiple workflow definitions; the engine selects the matching one per entity using the workflow-level criterion field evaluated at entity creation time. When no criterion matches, the engine uses the default built-in workflow.
The engine executes automatically after every entity write. It sets the initial state, evaluates automated transitions (cascade), and invokes processors on each transition. Manual transitions are triggered by the client via PUT /entity/{format}/{entityId}/{transition}.
The engine enforces a per-state visit limit of 10 by default (configurable via WithMaxStateVisits) and an absolute cascade depth limit of 100 to prevent infinite loops. Static cycle detection runs at import time.
WORKFLOW SCHEMA
Section titled “WORKFLOW SCHEMA”WorkflowDefinition (element of the workflows array in import):
{ "version": "1", "name": "prize-lifecycle", "desc": "State machine for Nobel Prize entities", "initialState": "NEW", "active": true, "criterion": null, "states": { "NEW": { "transitions": [ { "name": "APPROVE", "next": "APPROVED", "manual": true, "disabled": false, "criterion": null, "processors": [ { "type": "EXTERNAL", "name": "notify-approval", "executionMode": "SYNC", "config": { "attachEntity": true, "calculationNodesTags": "approval-service", "responseTimeoutMs": 30000, "retryPolicy": "", "context": "" } } ] }, { "name": "AUTO_VALIDATE", "next": "VALIDATED", "manual": false, "disabled": false, "criterion": { "type": "simple", "jsonPath": "$.year", "operatorType": "EQUALS", "value": "2024" }, "processors": [] } ] }, "APPROVED": { "transitions": [] }, "VALIDATED": { "transitions": [] } }}WorkflowDefinition fields:
version— string — schema version tag (informational; not interpreted by the engine)name— string — unique within the model; the primary key for MERGE modedesc— string — optional descriptioninitialState— string — state assigned when the entity is first created; must exist instatesactive— boolean — whenfalse, the engine skips this workflow during selectioncriterion—ConditionJSON ornull— evaluated against the entity at creation to select this workflow;nullmatches all entitiesstates— object — map of state name →StateDefinition
StateDefinition:
transitions— array ofTransitionDefinition— may be empty
TRANSITIONS
Section titled “TRANSITIONS”TransitionDefinition fields:
name— string — transition name; used by the client inPUT /entity/{format}/{entityId}/{name}and in engine cascadenext— string — target state; must exist instatesmanual— boolean —truemeans the transition requires an explicit client request;falsemeans the engine evaluates it automatically in cascadedisabled— boolean — whentrue, the engine skips this transition entirelycriterion—ConditionJSON ornull— evaluated before executing the transition;nullmeans always matches; the same Condition DSL as search (seesearchtopic)processors— array ofProcessorDefinition— invoked sequentially on this transition
PROCESSORS
Section titled “PROCESSORS”ProcessorDefinition fields:
type— string — processor type; see valid values belowname— string — logical processor nameexecutionMode— string — execution mode; see valid values belowconfig—ProcessorConfig
Valid type values (exhaustive for v0.6.1):
"EXTERNAL"— dispatches to a calculation node via gRPC usingcalculationNodesTagsfor routing
No other types are supported. Supplying any other value produces errors.VALIDATION_FAILED at workflow import time.
Valid executionMode values (exhaustive):
"SYNC"— the engine dispatches the processor and blocks until a response is received; the entity write transaction remains open during the wait; processor failure (including timeout andsuccess=falsein the response) returnserrors.WORKFLOW_FAILED(400) and the entity remains in the source state"ASYNC_SAME_TX"— same dispatch mechanics asSYNC(blocks inline, transaction stays open); failure semantics are identical toSYNC"ASYNC_NEW_TX"— dispatched within a savepoint; on failure the savepoint is rolled back and the error is logged as a warning; the pipeline continues to the next processor and the transition completes; returned entity modifications are discarded
An invalid executionMode value is treated as SYNC / ASYNC_SAME_TX (the engine’s default branch). It is not rejected at import time but produces undefined behaviour and must not be relied upon.
ProcessorConfig fields:
attachEntity— boolean — whentrue, the full entity payload is sent to the processorcalculationNodesTags— string — comma-separated tags for routing to registered calculation nodes; the engine selects a node that declares all required tags; returnserrors.NO_COMPUTE_MEMBER_FOR_TAGif no node matchesresponseTimeoutMs— int64 — timeout in milliseconds forSYNCprocessor response;0means use node defaultretryPolicy— string — retry policy name (plugin/platform-defined); empty means no retrycontext— string — arbitrary string forwarded to the processor as context metadata
CRITERIA
Section titled “CRITERIA”Criteria on workflows and transitions use the same Condition DSL as search. All four condition types are supported: simple, lifecycle, group, array. Criteria are evaluated in-memory against the entity’s JSON payload and lifecycle metadata.
simple criteria match entity data fields via JSONPath. lifecycle criteria match state, creationDate, or previousTransition from entity metadata.
A null criterion on a workflow means the workflow matches any entity. A null criterion on a transition means the transition always fires (automated) or is always available (manual). When multiple automated transitions are eligible, the engine selects the first one by declaration order whose criterion matches. A null criterion matches unconditionally, so a null-criterion automated transition must be the last automated transition in declaration order; any automated transitions declared after a null-criterion transition are unreachable.
IMPORT REQUEST
Section titled “IMPORT REQUEST”POST /api/model/{entityName}/{modelVersion}/workflow/import
entityName(path): stringmodelVersion(path): int32
Request body (application/json):
{ "importMode": "MERGE", "workflows": [ { ...WorkflowDefinition... } ]}importMode—"MERGE"(default): incoming workflows overwrite existing ones by name; existing workflows not in the import are preserved."REPLACE": all existing workflows are discarded; only the incoming set is stored."ACTIVATE": incoming workflows replace same-named existing ones and are setactive=true; existing workflows not in the import set are setactive=false.workflows— array ofWorkflowDefinition; all imported workflows are setactive=trueregardless of theactivefield in the body
Static validation runs before saving: definite infinite loops (cycles reachable only via automated transitions) cause 400 VALIDATION_FAILED.
Response: 200 OK, application/json:
{"success": true}EXPORT RESPONSE
Section titled “EXPORT RESPONSE”GET /api/model/{entityName}/{modelVersion}/workflow/export
Response: 200 OK, application/json:
{ "entityName": "nobel-prize", "modelVersion": 1, "workflows": [ { ...WorkflowDefinition... } ]}Returns 404 WORKFLOW_NOT_FOUND when no workflows have been imported for the model.
Export field omission: The export response omits optional fields that were not explicitly set or are default values. Specifically, TransitionDefinition objects in the export may omit disabled (when false) and processors (when empty). States with no transitions are serialised as {} rather than {"transitions":[]}. The desc field on WorkflowDefinition is omitted when empty.
ENGINE EXECUTION
Section titled “ENGINE EXECUTION”The workflow engine runs synchronously within the entity write transaction. The execution sequence for a CREATE:
- Load workflow definitions for the model.
- Evaluate each workflow’s
criterionagainst the entity; select the first match. If none match, use the built-in default workflow. - Set
entity.Meta.State = workflow.initialState. - If a named transition was requested (by the client), execute it: evaluate
criterion, invoke processors, setentity.Meta.State = transition.next. - Cascade: repeatedly scan the current state’s transitions; for each automated (
manual=false) non-disabled transition, evaluatecriterion; if it matches, invoke processors and advance the state. Stop when no automated transition matches or the state has no automated transitions. - The engine records
StateMachineEvententries to the audit log under the entity’stransactionId.
Per-state visit limit (default 10) and total cascade depth limit (100) are enforced to prevent infinite loops.
ERRORS
Section titled “ERRORS”errors.TRANSITION_NOT_FOUND—404— named transition does not exist in the current state’s workflowerrors.WORKFLOW_NOT_FOUND—404— no workflows found for the model (export endpoint)errors.WORKFLOW_FAILED— workflow engine encountered an unrecoverable error during executionerrors.NO_COMPUTE_MEMBER_FOR_TAG— no registered calculation node matches the requiredcalculationNodesTagserrors.COMPUTE_MEMBER_DISCONNECTED— a calculation node disconnected during processor dispatcherrors.VALIDATION_FAILED—400— static cycle detection failed during workflow import
EXAMPLES
Section titled “EXAMPLES”Import a workflow:
curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "importMode": "MERGE", "workflows": [ { "version": "1", "name": "prize-lifecycle", "initialState": "NEW", "active": true, "states": { "NEW": { "transitions": [ { "name": "APPROVE", "next": "APPROVED", "manual": true, "processors": [] } ] }, "APPROVED": { "transitions": [] } } } ] }' \ "http://localhost:8080/api/model/nobel-prize/1/workflow/import"Export workflows:
curl -s -H "Authorization: Bearer $TOKEN" \ "http://localhost:8080/api/model/nobel-prize/1/workflow/export"Trigger a manual transition:
curl -s -X PUT \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"category":"physics","year":"2024"}' \ "http://localhost:8080/api/entity/JSON/74807f00-ed0d-11ee-a357-ae468cd3ed16/APPROVE"Replace all workflows:
curl -s -X POST \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "importMode": "REPLACE", "workflows": [ { "version": "1", "name": "simple-wf", "initialState": "OPEN", "active": true, "states": { "OPEN": { "transitions": [] } } } ] }' \ "http://localhost:8080/api/model/nobel-prize/1/workflow/import"SEE ALSO
Section titled “SEE ALSO”- models
- crud
- grpc
- search
- errors.TRANSITION_NOT_FOUND
- errors.WORKFLOW_NOT_FOUND
- errors.WORKFLOW_FAILED
- errors.NO_COMPUTE_MEMBER_FOR_TAG
- errors.COMPUTE_MEMBER_DISCONNECTED
See also
Section titled “See also”cyoda help models— A model is a named, versioned schema registered per tenant. Every entity in the system is an instance of exactly one model. Models are identified by(entityName, modelVersion). The model ID is a deterministic UUID v5 derived from that key:UUID.newSHA1(NameSpaceURL, "{entityName}.{modelVersion}").cyoda help crud— Entities are instances of models. Each entity has a UUID, a model reference (entityName,modelVersion), and a lifecycle state managed by the workflow engine. Creating an entity requires the referenced model to be inLOCKEDstate. All write operations run within a Cyoda transaction and return atransactionIdalongside the affected entity IDs.cyoda help grpc— cyoda-go exposes one gRPC service:CloudEventsService(packageorg.cyoda.cloud.api.grpc). All gRPC methods use the CloudEvents Protobuf envelope (io.cloudevents.v1.CloudEvent) as both request and response types. The event type string in the CloudEvent envelope selects the operation; the JSON payload intext_data(orbinary_data) carries the operation-specific body.cyoda help search— Search operates against a specific entity model(entityName, modelVersion). Two modes are supported:cyoda help errors TRANSITION_NOT_FOUND— Entity workflow state machines define explicit transitions between states. This error fires when a transition is triggered that does not exist in the model’s workflow definition for the entity’s current state. Also occurs when the transition name is misspelled or when the entity is in a terminal state that allows no further transitions.cyoda help errors WORKFLOW_NOT_FOUND— Entity models reference a workflow by name to govern state transitions. This error is returned when the named workflow cannot be found in the tenant’s workflow registry, during entity type registration or when a model references a workflow that was deleted.cyoda help errors WORKFLOW_FAILED— During an entity create or transition operation the associated workflow processors (pre-processors, post-processors) or guard conditions ran but one of them signalled failure. The failure message from the processor is included in the error detail.cyoda help errors NO_COMPUTE_MEMBER_FOR_TAG— Workflow processors are dispatched to nodes that advertise matching compute tags. When no node with the required tag is alive in the cluster within the configured wait timeout (CYODA_DISPATCH_WAIT_TIMEOUT), the operation is rejected with this error.cyoda help errors COMPUTE_MEMBER_DISCONNECTED— The compute member responsible for executing a processor or workflow step disconnected before completing the operation. The task may or may not have been executed.
Raw formats
Section titled “Raw formats”/help/workflows.json— full descriptor (matchesGET /help/{topic}envelope)/help/workflows.md— body only