Skip to main content

Processes

Stateway supports three formats for process definitions: BPMN 2.0 XML, JSON, and YAML. All three are parsed into the same internal model and run on the same engine. BPMN 2.0 XML is the canonical format — JSON and YAML express a subset of what it can model. They are convenience formats for the same concepts, never a superset with different semantics. See JSON/YAML vs BPMN — Known Limitations for what BPMN can express that JSON/YAML cannot.

Supported Formats

Formatsource_typeBest For
BPMN 2.0 XMLbpmnStandard interchange; compatible with Camunda Modeler, bpmn.io
JSONjsonProgrammatic creation; AI agents and automated pipelines
YAMLyamlHuman-readable authoring
BPMN (base64)bpmn-base64Transmitting binary BPMN files as JSON strings

JSON/YAML vs BPMN — Known Limitations

BPMN 2.0 XML is the canonical format. JSON and YAML cover the same execution model, but a few BPMN capabilities have no JSON/YAML equivalent. When you need one of these, author the process — or just the affected elements — in BPMN 2.0 XML.

CapabilityBPMN 2.0 XMLJSON / YAML
Typed I/O specifications — named, typed activity inputs/outputs, form auto-generation, and typed output validation✅ via ioSpecification + itemDefinition❌ Declare the form explicitly with formSchema; task outputs are not type-validated. See Typed I/O & Form Schemas.
Data objects & data storesitemDefinition, dataObject, dataStoreReference✅ parsed from XML❌ Not expressible in JSON/YAML. See Types & Validation.
Gateway default flow attribute — a named fallback flow✅ via default="flowId"❌ No named default; the fallback is the first outgoing flow with no condition. See Expressions in Conditions.

Everything else — element types, flows, conditions, timers, assignments, service-task definitions, and variableFilter — behaves identically across all three formats.

Creating a Definition

JSON Format

The JSON source object requires a key, an elements array, and a flows array. Elements declare their outgoing flow IDs; flows declare their sourceRef and targetRef.

Request:

curl -X POST https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "expense-approval",
"name": "Expense Approval",
"source_type": "json",
"source": {
"key": "expense-approval",
"elements": [
{ "id": "start", "type": "startEvent", "outgoing": ["flow1"] },
{ "id": "review", "type": "userTask", "name": "Review Expense", "outgoing": ["flow2"] },
{ "id": "end", "type": "endEvent" }
],
"flows": [
{ "id": "flow1", "sourceRef": "start", "targetRef": "review" },
{ "id": "flow2", "sourceRef": "review", "targetRef": "end" }
]
}
}'

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "json",
"source_hash": "a3f8c2d1...",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
}

YAML Format

YAML source is a string that serializes the same structure as JSON:

Request:

curl -X POST https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "expense-approval",
"name": "Expense Approval",
"source_type": "yaml",
"source": "key: expense-approval\nelements:\n - id: start\n type: startEvent\n outgoing: [flow1]\n - id: review\n type: userTask\n name: Review Expense\n outgoing: [flow2]\n - id: end\n type: endEvent\nflows:\n - id: flow1\n sourceRef: start\n targetRef: review\n - id: flow2\n sourceRef: review\n targetRef: end\n"
}'

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "yaml",
"source_hash": "b4d9f7e2...",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
}

BPMN XML Format

To generate well-formed BPMN 2.0 XML with correct element IDs, namespace declarations, and Stateway extensions, use the bpmn-xml-generator Claude skill.

Pass the raw BPMN XML as a string in source:

Request:

curl -X POST https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "expense-approval",
"name": "Expense Approval",
"source_type": "bpmn",
"source": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><bpmn:definitions xmlns:bpmn=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:stateway=\"https://stateway.io/schema/bpmn/1.0\"><bpmn:process id=\"expense-approval\" isExecutable=\"true\"><bpmn:startEvent id=\"start\"><bpmn:outgoing>flow1</bpmn:outgoing></bpmn:startEvent><bpmn:userTask id=\"review\" name=\"Review Expense\"><bpmn:incoming>flow1</bpmn:incoming><bpmn:outgoing>flow2</bpmn:outgoing></bpmn:userTask><bpmn:endEvent id=\"end\"><bpmn:incoming>flow2</bpmn:incoming></bpmn:endEvent><bpmn:sequenceFlow id=\"flow1\" sourceRef=\"start\" targetRef=\"review\"/><bpmn:sequenceFlow id=\"flow2\" sourceRef=\"review\" targetRef=\"end\"/></bpmn:process></bpmn:definitions>"
}'

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "bpmn",
"source_hash": "c6e8a1b3...",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
}

BPMN (base64) Format

If you're loading a BPMN file from disk or transmitting it over a JSON-only channel, use source_type: "bpmn-base64". The API base64-decodes the source string to UTF-8 XML before parsing — the result is identical to sending source_type: "bpmn" directly. The stored source_type in the response will be "bpmn" (the decoded form).

To encode a file: base64 expense-approval.bpmn

Request:

curl -X POST https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "expense-approval",
"name": "Expense Approval",
"source_type": "bpmn-base64",
"source": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48YnBtbjpkZWZpbml0aW9ucy..."
}'

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "bpmn",
"source_hash": "c6e8a1b3...",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
}

JSON/YAML Element Reference

Each element in the elements array supports these fields:

FieldTypeRequiredDescription
idstringUnique identifier within the process
typestringElement type (see below)
namestringHuman-readable label
outgoingstring[]IDs of outgoing flows
timerTypedate | duration | cycleFor timerEvent elements
timerExpressionstringISO 8601 date, duration, or cycle expression
assigneestringFor userTask — supports {{variables.x}}
taskDefinitionobjectFor serviceTask / businessRuleTask
taskHeadersobjectKey-value headers for service tasks
ioMappingobjectInput/output variable mapping
formSchemaobjectFor userTask — JSON Schema for the task form, rendered by the Frontend Gateway. In JSON/YAML you declare it explicitly; there is no auto-generation from types (see below).

Typed I/O & Form Schemas (BPMN XML only)

BPMN 2.0 XML lets you attach a typed I/O specification to an activity — named, typed inputs and outputs (ioSpecification with dataInput/dataOutput, whose types come from itemDefinition declarations). JSON and YAML definitions express a subset of the process model and cannot declare this typed contract. Two behaviors that depend on it are therefore unavailable when a process is authored in JSON/YAML:

  • Form auto-generation. In BPMN, a userTask with a typed I/O specification gets its form generated automatically from the declared inputs and their types. In JSON/YAML there is no typed I/O specification, so you must declare the form explicitly with the formSchema field on the userTask element.
  • Typed output validation. In BPMN, the outputs submitted when a task or service task completes are validated against the activity's declared outputSets and types. Activities authored in JSON/YAML have no typed contract, so their outputs are accepted without type validation.

This is a documented limitation, not a bug: JSON/YAML are convenience formats for the same execution model, and they offer a subset of what BPMN XML can express — never a superset. If you need typed inputs/outputs, enumerations, or output validation, author the process (or at least the typed activities) in BPMN 2.0 XML. See Types & Validation for the full typed-modeling guide.

Supported Element Types

For the full, always up-to-date list of every element, field, and its support status, see the Supported Elements & Fields reference.

TypeDescription
startEventProcess entry point
endEventProcess termination
taskGeneric/abstract task — completes immediately and continues to the next flow (no side effects)
userTaskHuman task requiring manual action
serviceTaskAutomated HTTP call (sync or async)
sendTaskFire-and-forget HTTP call — the token advances immediately and the response is never mapped into variables. The response is still recorded, and is subject to the same 5 MB cap as a service task: a larger response is discarded and the call is recorded as a failure — even though the request itself was delivered. The process continues either way
businessRuleTaskEvaluates a Decision Model (DMM)
exclusiveGatewayXOR — exactly one outgoing branch taken
parallelGatewayAND — all outgoing branches taken, waits for all to complete
inclusiveGatewayOR-split — evaluates conditions, can take multiple paths simultaneously; when no condition matches, routes to the default flow (the BPMN default attribute, or in JSON/YAML the first outgoing flow with no condition)
timerEventPauses until a time condition is met (as startEvent or intermediateCatchEvent; see caveat below)
intermediateThrowEventEmits a notification:published event (delivered via webhook and WebSocket), including the variables named in variableFilter when present
boundaryEventAttached to an activity. Only Error boundary events fire today — they catch an output-schema ValidationError from the attached businessRuleTask or serviceTask and route to an alternate flow. Timer, Message, Signal, and Escalation boundary events are parsed but never trigger (see caveat below)
caution

intermediateCatchEvent only fires for Timer. Message, Signal, and Conditional catch events are recognized but pass through immediately (no correlation mechanism yet — see Unsupported Element Types).

Boundary events only fire for Error. Timer, Message, Signal, and Escalation boundary events are parsed but never trigger — there is no mechanism today to schedule/cancel them against the host activity's lifecycle. Planned, not yet supported.

Unsupported Element Types (yet)

The following BPMN 2.0 element types are not yet fully supported. Unrecognized types are silently skipped during parsing (the element and its outgoing flows are excluded from the process model). Recognized-but-unhandled types pass through immediately without executing any action.

Recognized but not executed:

TypePurposeNotes
scriptTaskExecute an inline script (Groovy, JavaScript, etc.)Completes immediately without executing any script
receiveTaskWait for an inbound message before continuingCompletes immediately without waiting
eventBasedGatewayBranch based on whichever event arrives firstPasses through
subProcessEncapsulate a nested flow inside a collapsed sub-processCompletes immediately without executing nested flow
callActivityInvoke a reusable child process definitionCompletes immediately without calling the child process

Not recognized (silently skipped during parsing):

TypePurposeNotes
messageFlowMessage-based communication between participantsNot supported
laneSet / laneVisual swimlane organization of elementsNot supported
compensationEventTrigger compensating actions on rollbackNot supported
escalationEventEscalate to a parent process scopeNot supported
linkEventJump between points in the same process (go-to)Not supported
complexGatewayMerge/split based on custom boolean conditionsNot supported
multiInstanceTaskExecute a task N times (loop / parallel fan-out)Not supported
Available in BPMN XML only

Data objects and data stores are a BPMN 2.0 XML feature. To declare typed process variables or query data from other process instances directly in the BPMN diagram, see Types & Validation. These constructs (itemDefinition, dataObject, dataStoreReference) are parsed only from BPMN XML — JSON/YAML definitions express a subset and cannot declare them.

Expressions in Conditions

Gateway flows can use the {{expression}} syntax in their condition field:

{
"flows": [
{ "id": "f-high", "sourceRef": "gw1", "targetRef": "high-value", "condition": "{{variables.amount > 10000}}" },
{ "id": "f-std", "sourceRef": "gw1", "targetRef": "standard", "condition": "{{variables.amount <= 10000}}" }
]
}

Format differences:

  • BPMN XML: Conditions are placed on bpmn:sequenceFlow elements using bpmn:conditionExpression. Gateways support a default="flowId" attribute to specify the fallback flow when no condition matches.
  • JSON/YAML: Use the condition field on flow objects. There is no default flow attribute equivalent in JSON/YAML format.

Both formats evaluate conditions using the same underlying mechanism.

Available context: variables, instance, now(). Operators: ==, !=, >, <, >=, <=, &&, ||, !

Listing Definitions

Request:

curl https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key"

Response:

{
"data": [
{
"id": "def_01j...",
"key": "expense-approval",
"version": 2,
"name": "Expense Approval",
"source_type": "json",
"source_hash": "a3f8c2d1...",
"is_active": true,
"created_at": "2026-04-26T12:00:00.000Z"
}
]
}

Versioning

Every PUT to an existing definition creates a new version. Previous versions remain accessible. GET /definitions/:key always returns the currently active version.

List All Versions

Request:

curl https://api.stateway.io/v1/definitions/expense-approval/versions \
-H "X-API-Key: sw_live_your_key"

Response:

{
"data": [
{
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "json",
"source_hash": "a3f8c2d1...",
"is_active": false,
"created_at": "2026-04-26T12:00:00.000Z"
},
{
"id": "def_02k...",
"key": "expense-approval",
"version": 2,
"name": "Expense Approval v2",
"source_type": "json",
"source_hash": "b9d7f3e5...",
"is_active": true,
"created_at": "2026-05-01T09:00:00.000Z"
}
]
}

Get a Specific Version

Request:

curl https://api.stateway.io/v1/definitions/expense-approval/versions/1 \
-H "X-API-Key: sw_live_your_key"

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"name": "Expense Approval",
"source_type": "json",
"source_hash": "a3f8c2d1...",
"is_active": false,
"created_at": "2026-04-26T12:00:00.000Z"
}
}

Create a New Version

Request:

curl -X PUT https://api.stateway.io/v1/definitions/expense-approval \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Expense Approval v2",
"source_type": "json",
"source": {
"key": "expense-approval",
"elements": [
{ "id": "start", "type": "startEvent", "outgoing": ["flow1"] },
{ "id": "review", "type": "userTask", "name": "Review Expense", "outgoing": ["flow2"] },
{ "id": "approve", "type": "serviceTask", "name": "Notify Finance", "outgoing": ["flow3"] },
{ "id": "end", "type": "endEvent" }
],
"flows": [
{ "id": "flow1", "sourceRef": "start", "targetRef": "review" },
{ "id": "flow2", "sourceRef": "review", "targetRef": "approve" },
{ "id": "flow3", "sourceRef": "approve", "targetRef": "end" }
]
}
}'

Response:

{
"data": {
"id": "def_02k...",
"key": "expense-approval",
"version": 2,
"name": "Expense Approval v2",
"source_type": "json",
"source_hash": "b9d7f3e5...",
"is_active": true,
"created_at": "2026-05-01T09:00:00.000Z"
}
}

New instances use the latest active version. Existing instances continue running on the version they were started with.

Rollback to a Previous Version

Atomically reactivate a previous version, deactivating the current one:

Request:

curl -X POST https://api.stateway.io/v1/definitions/expense-approval/rollback \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "version": 1 }'

Response:

{
"data": {
"id": "def_01j...",
"key": "expense-approval",
"version": 1,
"is_active": true,
"updated_at": "2026-05-10T08:00:00.000Z"
}
}

After rollback, new instances will use version 1. The next PUT (new version) will be assigned MAX(version) + 1 — in this case version 3 — to avoid collision with existing version numbers.

Instances that were already running are not affected — they remain bound to the definition_id they were started with.

tip

See Process Versioning for a full guide on version management strategies.

Exporting as BPMN XML

Export any definition back to BPMN XML (regardless of original format):

Request:

curl https://api.stateway.io/v1/definitions/expense-approval/xml \
-H "X-API-Key: sw_live_your_key" \
-o expense-approval.bpmn

The response has Content-Type: application/xml with the BPMN 2.0 XML document.

Auto-Layout

Add ?layout=true to automatically inject diagram coordinates using bpmn-auto-layout. The result can be opened immediately in bpmn.io or Camunda Modeler without any manual positioning:

Request:

curl "https://api.stateway.io/v1/definitions/expense-approval/xml?layout=true" \
-H "X-API-Key: sw_live_your_key" \
-o expense-approval.bpmn

To export a specific version:

Request:

curl "https://api.stateway.io/v1/definitions/expense-approval/xml?version=1" \
-H "X-API-Key: sw_live_your_key" \
-o expense-approval-v1.bpmn

Mermaid Preview

Generate a Mermaid flowchart diagram from a definition — useful for documentation, sharing, and visual review:

Request:

curl https://api.stateway.io/v1/definitions/expense-approval/preview.mermaid \
-H "X-API-Key: sw_live_your_key"

The response is text/plain Mermaid syntax:

flowchart TD
start([start]) --> flow1
flow1 --> review[Review Expense]
review --> flow2
flow2 --> end([end])

To preview a specific version:

Request:

curl "https://api.stateway.io/v1/definitions/expense-approval/preview.mermaid?version=2" \
-H "X-API-Key: sw_live_your_key"
info

The Mermaid output is for visualization only — the BPMN (or JSON/YAML) definition remains the source of truth. It is not re-imported.

Deleting a Definition

Request:

curl -X DELETE https://api.stateway.io/v1/definitions/expense-approval \
-H "X-API-Key: sw_live_your_key"

Returns 204 No Content. This is a soft delete — running instances continue to execute on their original version. The definition key can be reused by creating a new definition with the same key.

Best Practices

  • Use descriptive keysexpense-approval not proc1
  • Use meaningful element IDsreview-expense not node3
  • Add names to all elements — improves readability in API responses, logs, and task lists
  • Start with JSON/YAML — easier to version-control and author programmatically; export to BPMN when visual editing is needed
  • Use ?layout=true on first export — saves time positioning elements in bpmn.io or Camunda Modeler
  • Version deliberately — each version is immutable; use rollback to revert rather than deleting versions