Inline Types
A type in Stateway is a named schema embedded inside a <bpmn:itemDefinition> element. Types are declared once at the top of the BPMN document and referenced from data objects, task inputs, and task outputs.
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.
Declaring a Type
All <bpmn:itemDefinition> elements go inside <bpmn:definitions>, before the process body:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:stateway="https://stateway.io/schema/bpmn/1.0"
targetNamespace="https://stateway.io/processes/my-process">
<bpmn:itemDefinition id="MyType" structureRef="..." />
<bpmn:process id="my-process" isExecutable="true">
<!-- process body -->
</bpmn:process>
</bpmn:definitions>
The structureRef attribute determines the type's flavor.
Flavor 1: XSD Primitives
For simple scalars, reference an XSD primitive. No schema body is needed:
<!-- A price field — must be a number -->
<bpmn:itemDefinition id="Price" structureRef="xsd:decimal" />
<!-- A boolean flag -->
<bpmn:itemDefinition id="IsUrgent" structureRef="xsd:boolean" />
<!-- A date-time string -->
<bpmn:itemDefinition id="DueDate" structureRef="xsd:dateTime" />
Supported XSD primitives:
structureRef | Validates As |
|---|---|
xsd:string | Any string |
xsd:decimal | Any number (integer or float) |
xsd:int | Integer |
xsd:boolean | true or false |
xsd:dateTime | ISO 8601 date-time string |
xsd:date | ISO 8601 date string |
xsd:time | ISO 8601 time string |
xsd:base64Binary | Base64-encoded string, up to MAX_BASE64_VARIABLE_BYTES (default 1 MB, decoded) — see Binary and File Data below |
Flavor 2: Inline JSON Schema
For enumerations and objects, set structureRef="stateway:inline" and embed a JSON Schema (draft 2020-12) inside a <stateway:schema> element:
Enumeration
<bpmn:itemDefinition id="LoanStatus" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="enum"><![CDATA[
{
"type": "string",
"enum": ["draft", "submitted", "in_review", "approved", "rejected", "disbursed"]
}
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
Object
<bpmn:itemDefinition id="Applicant" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="object"><![CDATA[
{
"type": "object",
"required": ["fullName", "email", "documentId"],
"properties": {
"fullName": { "type": "string", "minLength": 2 },
"email": { "type": "string", "format": "email" },
"documentId": { "type": "string", "pattern": "^[0-9]{11}$" },
"phone": { "type": "string" }
},
"additionalProperties": false
}
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
Primitive with Constraints
<!-- A non-negative decimal with 2 decimal places max -->
<bpmn:itemDefinition id="Money" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="primitive"><![CDATA[
{
"type": "number",
"minimum": 0,
"multipleOf": 0.01
}
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
The kind attribute is a hint for documentation and form generation:
kind | Meaning |
|---|---|
enum | Fixed set of valid string (or scalar) values |
object | Structured object with named fields |
primitive | Scalar with constraints beyond what the XSD primitive allows |
reference | Pointer to another instance (used with DataStore) |
Collections
Add isCollection="true" to wrap the schema in an array type. The item schema stays the same — the engine handles the array wrapping automatically:
<!-- A list of documents -->
<bpmn:itemDefinition id="DocumentList" 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>
A variable typed as DocumentList must be a JSON array; each element must match the item schema.
Binary and File Data
There are two ways to get binary data into a process instance, and they're two different storage strategies — declaring one over the other doesn't turn on the other's behavior:
Base64 in a variable (xsd:base64Binary) | Files API (stateway:file) | |
|---|---|---|
| Where the data lives | Inside instance.variables, like any other value | In object storage; the variable only holds a reference |
| Does the binary pass through the Stateway API? | Yes — it's part of the JSON body | No — the client uploads directly to a pre-signed storage URL |
| Counts against the tenant's storage quota? | No | Yes |
| Download endpoint? | No — read it back from the variable itself | Yes — a dedicated redirect endpoint |
| Deletion leaves a tombstone? | No — it's overwritten/removed like any variable | Yes — a file_ref_deleted tombstone replaces the value |
| Automatic retention/expiry? | No | Yes — files belonging to finished instances are eventually removed |
| Excluded from audit snapshots? | Only if the variable name ends in _binary, _blob, or _base64 | Not applicable — no raw bytes ever reach a snapshot to begin with |
Both structureRef values in this table are declarable and validated. But declaring the type is not what turns on any of the Files API behavior in the right column — quota accounting, the download endpoint, tombstones, and retention are all driven by the upload flow itself (POST /instances/:id/files → confirm), independent of whether the target variable has a declared type at all. What declaring stateway:file adds is:
- Shape validation. The variable must hold either a live file reference (the
file_refobject the Files API writes on confirm) or its deletion tombstone — anything else is rejected on assignment, the same way any other typed variable is. See Thefile_refVariable Value for the exact fields. - A guard on the upload itself. Requesting an upload slot for a variable that's declared with an incompatible type (a different scalar type, or a
stateway:filecollection) is rejected with422 VARIABLE_TYPE_MISMATCHbefore any bytes are transferred. A variable with no declared type (ad-hoc) still accepts uploads exactly as before.
An itemDefinition with a structureRef the parser doesn't recognize is still accepted without validation — this remains true for any token outside the two covered here (and outside stateway:inline/stateway:catalog/*, which have their own dedicated handling). Don't rely on an unrecognized structureRef to protect a variable; it silently validates nothing.
If you embed base64 data directly in a variable, xsd:base64Binary caps it at MAX_BASE64_VARIABLE_BYTES (default 1 MB, decoded) per variable, in addition to the overall 5 MB instance variables payload limit described in the File Variables guide. To keep it out of audit log snapshots, name the field with a _base64 suffix (the same convention used for _binary and _blob fields — see Audit Log).
For anything beyond a small embedded value, use the Files API instead.
Validation at Upload Time
When you POST /v1/definitions with a BPMN that contains itemDefinition elements, Stateway validates each schema immediately. If any schema is malformed, the upload is rejected with a descriptive error — invalid definitions never reach production.
Common rejection reasons:
| Cause | Example Error |
|---|---|
structureRef="stateway:inline" with no <stateway:schema> | itemDefinition "MyType": missing stateway:schema |
Invalid JSON inside <stateway:schema> | itemDefinition "MyType": schema is not valid JSON |
| Schema fails JSON Schema meta-validation | itemDefinition "MyType": "minLength" requires integer |
External $ref used | itemDefinition "MyType": external $ref not allowed |
structureRef="stateway:catalog/..." | Not yet supported |
Limits
To protect against abuse, the following limits apply (all configurable via environment variables):
| Limit | Default |
|---|---|
Max itemDefinition elements per document | 200 |
Max schema size per itemDefinition | 64 KB |
| Max schema nesting depth | 20 levels |
External $ref URIs | Not allowed |
Complete Example
A self-contained BPMN with multiple type flavors:
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:stateway="https://stateway.io/schema/bpmn/1.0"
targetNamespace="https://stateway.io/processes/loan-application">
<!-- Primitive XSD types — no schema body needed -->
<bpmn:itemDefinition id="Money" structureRef="xsd:decimal" />
<bpmn:itemDefinition id="DueDate" structureRef="xsd:date" />
<!-- Enum -->
<bpmn:itemDefinition id="LoanStatus" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="enum"><![CDATA[
{ "type": "string", "enum": ["draft", "approved", "rejected"] }
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
<!-- Object -->
<bpmn:itemDefinition id="Applicant" structureRef="stateway:inline">
<bpmn:extensionElements>
<stateway:schema kind="object"><![CDATA[
{
"type": "object",
"required": ["fullName", "email"],
"properties": {
"fullName": { "type": "string", "minLength": 2 },
"email": { "type": "string", "format": "email" }
}
}
]]></stateway:schema>
</bpmn:extensionElements>
</bpmn:itemDefinition>
<!-- Collection -->
<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:process id="loan-application" isExecutable="true">
<!-- process elements -->
</bpmn:process>
</bpmn:definitions>
This file is self-contained: export it, re-import it, and all type validation works identically — no external registry required.
Enum values freeze with the process version. If you add a new valid status to LoanStatus, deploy a new version of the process definition. Running instances continue using the enum values from the version they were started with.