Data Objects
A data object is a process-level variable with a declared type. It bridges an inline type and the actual runtime value stored in the process instance.
These constructs (itemDefinition, dataObject/dataObjectReference, dataStore/dataStoreReference)
are parsed only from BPMN 2.0 XML definitions. JSON/YAML process definitions express a subset and
cannot declare typed data objects, data stores, or inline types. See the
Processes guide for JSON/YAML format limitations.
Two BPMN Elements
BPMN 2.0 uses two elements together:
<bpmn:dataObject>— declared inside<bpmn:process>, names the variable and references its type.<bpmn:dataObjectReference>— a positional "sighting" of that variable at a specific point in the flow. Multiple references can point to the same dataObject.
<bpmn:process id="loan-application" isExecutable="true">
<!-- Declare the typed variables -->
<bpmn:dataObject id="ApplicationData" name="application" itemSubjectRef="Applicant" />
<bpmn:dataObject id="StatusData" name="loan_status" itemSubjectRef="LoanStatus" />
<!-- Place them in the flow -->
<bpmn:dataObjectReference id="ApplicationDataRef" dataObjectRef="ApplicationData" />
<bpmn:dataObjectReference id="StatusDataRef" dataObjectRef="StatusData" />
</bpmn:process>
The name attribute of a dataObject becomes the key in instance.variables. In the example above, instance.variables.application is validated against the Applicant type on every write.
Coexistence with Untyped Variables
Typed and untyped variables live together in the same variables map. Only variables declared as dataObject are validated — everything else passes through freely:
# "internal_counter" is not declared as a dataObject — it passes through without validation
curl -X POST https://api.stateway.io/v1/instances \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"definition_key": "loan-application",
"variables": {
"application": {
"fullName": "Ana Lima",
"email": "ana@example.com"
},
"internal_counter": 0
}
}'
application is validated against the Applicant schema. internal_counter is stored as-is.
Validation on Write
Whenever a typed variable is written — at instance creation, task completion, or service task output — Stateway validates the value against the declared type before merging it into instance.variables.
Invalid write example:
curl -X POST https://api.stateway.io/v1/instances \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"definition_key": "loan-application",
"variables": {
"application": {
"fullName": "A",
"email": "not-an-email"
}
}
}'
Response 400 Bad Request:
{
"error": "ValidationError",
"errors": [
{ "path": "application.fullName", "message": "must NOT have fewer than 2 characters" },
{ "path": "application.email", "message": "must match format \"email\"" }
]
}
The instance is not created. No partial state is written.
dataState — Lifecycle Annotation
Each dataObjectReference optionally carries a dataState attribute. It has no effect on validation or execution — it's an annotation for the human reader:
<bpmn:dataObjectReference
id="ApplicationDataRef_collected"
dataObjectRef="ApplicationData"
dataState="collected" />
<bpmn:dataObjectReference
id="ApplicationDataRef_reviewed"
dataObjectRef="ApplicationData"
dataState="reviewed" />
Using two references for the same dataObject with different states is a BPMN convention for showing lifecycle transitions in the diagram. Both point to the same application variable at runtime.
Collections
When the referenced itemDefinition has isCollection="true", the variable must be a JSON array, and each element is validated individually:
<bpmn:itemDefinition id="Documents" structureRef="stateway:inline" isCollection="true">
<bpmn:extensionElements>
<stateway:schema kind="object"><![CDATA[
{
"type": "object",
"required": ["name", "url"],
"properties": {
"name": { "type": "string" },
"url": { "type": "string", "format": "uri" }
}
}
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
<bpmn:dataObject id="DocListData" name="documents" itemSubjectRef="Documents" />
Writing documents with a non-array value, or with an array element missing name, produces a ValidationError with the array index in the path (e.g., documents[1].url).
When to Use Typed Variables
| Use typed dataObject | Leave untyped |
|---|---|
| Business-critical fields (applicant, amount, status) | Temporary working variables |
| Fields shared across multiple tasks | One-off intermediate values |
| Enum values with a fixed set of valid options | Internal counters, flags |
| Fields received from external systems | Debug/logging metadata |
You can introduce typed variables incrementally. Adding a dataObject to an existing process only affects the named variable — everything else continues unchanged.