Spiff-Flavored BPMN¶
SpiffWorkflow uses the BPMN 2.0 specification to implement extensions that provide functionality to help run your processes, particularly with Script Tasks, Service Tasks, and User Tasks. These extensions are typically defined within the <bpmn:extensionElements> tag of a BPMN element.
Namespace¶
SpiffWorkflow extensions use the spiffworkflow namespace. Ensure this namespace is defined in your BPMN XML:
xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core"
XML Extension Name Reference¶
This section lists the spiffworkflow XML names that Spiff Arena uses.
It is meant as a quick naming reference, not a full behavior guide.
The usual pattern is lower camel case for XML element and attribute names.
For example, the modeler moddle type PreScript serializes as <spiffworkflow:preScript>.
Generic Arena task configuration is usually stored as lower-camel-case name values inside <spiffworkflow:property>.
SpiffWorkflow parses direct spiffworkflow:* elements inside <bpmn:extensionElements> into task_spec.extensions.
Some names are interpreted by SpiffWorkflow itself; others are Arena conventions that SpiffWorkflow only carries as extension data.
In the tables below:
“Extension element with text” means an element whose value is the text between the opening and closing XML tags, such as
<spiffworkflow:preScript>...</spiffworkflow:preScript>.“Nested under …” means the element is only used inside another
spiffworkflowelement, not directly under<bpmn:extensionElements>.“Extension attribute” means a
spiffworkflow:*attribute on a BPMN element. Those rows are the only attribute rows.“Engine behavior” means the SpiffWorkflow library has parser or runtime code that interprets the value. “Parsed extension data” means the library exposes the value in
task_spec.extensions, but Arena owns the behavior. “Arena/modeler only” means the name is used by Arena orbpmn-js-spiffworkflow, not by SpiffWorkflow engine code.
Elements and Attributes¶
These names are spiffworkflow XML elements or namespace attributes used by Arena.
Extension elements appear under <bpmn:extensionElements> unless the form says they are nested under another element.
XML name |
Form |
Arena use |
SpiffWorkflow library support |
|---|---|---|---|
|
Extension element with text |
Python to run before an activity or event task. |
Engine behavior. |
|
Extension element with text |
Python to run after an activity or event task. |
Engine behavior. |
|
Extension element with text |
DMN decision id for a business rule task. |
Engine behavior. |
|
Extension element with text |
Markdown/Jinja instructions rendered by Arena for human-facing tasks. |
Parsed extension data. Defined by the SpiffWorkflow library schema, but rendered by Arena. |
|
Extension element with text |
Python expression for message payload data. |
Engine behavior. |
|
Extension element with text |
Process variable name for received message payload data. |
Engine behavior. |
|
Extension element with text |
Payload expression on signal, error, and escalation definitions. |
Engine behavior. |
|
Extension element with text |
Variable name for user task form output or event payload output. |
Engine behavior. |
|
Extension element containing |
Message correlation against process data. |
Engine behavior. |
|
Nested under |
Correlation property id. |
Engine behavior. |
|
Nested under |
Correlation expression. |
Engine behavior. |
|
Extension element containing |
Generic name/value task configuration container. |
Parsed extension data. |
|
Nested under |
One generic task configuration value. |
Parsed extension data; property names are application conventions. |
|
Extension element with |
Connector/operator configuration for service tasks. |
Engine behavior. |
|
Nested under |
Parameter container. |
Engine behavior. |
|
Nested under |
One connector/operator parameter. |
Engine behavior. |
|
Nested under |
Retry settings for the service task operator. |
Engine behavior. |
|
Extension element containing |
Script task unit test container. |
Parsed extension data. |
|
Nested under |
One script task unit test. |
Parsed extension data. |
|
Nested under |
Unit test input JSON. |
Parsed extension data. |
|
Nested under |
Unit test expected output JSON. |
Parsed extension data. |
|
Extension element containing |
Human task metadata container. |
Parsed extension data. |
|
Nested under |
One evaluated human task metadata expression. |
Parsed extension data. |
|
Extension attribute on |
Run pre/post scripts on each loop instance instead of only the outer loop task. |
Engine behavior. |
|
Extension element with boolean text |
Allows a message start or user task to be completed through Arena public routes. |
Parsed extension data; Arena owns the behavior. |
|
Extension element with text |
Markdown/Jinja confirmation shown after public completion. |
Parsed extension data; Arena owns the behavior. |
|
Extension element with text |
Label for Arena signal event action buttons. |
Parsed extension data; Arena owns the behavior. |
|
Extension element with text |
Process model to start when an Arena user or manual task becomes available. |
Parsed extension data; Arena owns the behavior. |
|
Extension element with text on a data object |
Arena data object category used for process data display and access decisions. |
Arena/frontend behavior. |
|
Extension attribute on message-capable events/tasks |
Tells Arena message tooling that process-variable correlation matching is enabled. |
Arena/modeler only. |
|
Extension attribute on a multi-instance subprocess/activity |
Tells Arena/modeler whether multi-instance output data should be synchronized. |
Arena/modeler only. |
|
Extension attribute on |
Links a BPMN message to an Arena message JSON schema. |
Arena/modeler only. |
Extension attributes are valid BPMN extension points, but for new Arena task settings the safer default is usually a named element inside <bpmn:extensionElements>.
That path is parsed by the SpiffWorkflow library and gives the XML name its own domain meaning.
Use an extension attribute only when the value is a small flag or identifier that naturally belongs directly on an existing BPMN element and both the modeler and runtime code explicitly support it.
For task-availability hook behavior, see Start a Process When a Human Task Becomes Available.
Property Names¶
SpiffWorkflow parses <spiffworkflow:properties> into a generic dictionary.
Arena uses that generic SpiffWorkflow extension point for Arena-specific configuration values.
These are not separate XML elements; they are name attributes on <spiffworkflow:property>.
Property name |
Example XML |
Arena use |
SpiffWorkflow library support |
|---|---|---|---|
|
|
JSON Schema file for a user task form. |
SpiffWorkflow parses the generic property; Arena owns this property name and behavior. |
|
|
UI Schema file for a user task form. |
SpiffWorkflow parses the generic property; Arena owns this property name and behavior. |
Common Extensions¶
spiffworkflow:preScript and spiffworkflow:postScript¶
Many task types, including User Tasks and Service Tasks, can include pre-scripts and post-scripts. These are Python scripts executed before and after the main task logic, respectively. They operate within the task’s data context, meaning variables defined or modified in these scripts become part of the task’s data.
Example:
<bpmn:task id="MyTaskWithScripts" name="Task with Pre/Post Scripts">
<bpmn:extensionElements>
<spiffworkflow:preScript>
# Code to run before the task's main logic
my_custom_task_status = 'pending' # This variable is now in the task data
</spiffworkflow:preScript>
<spiffworkflow:postScript>
# Code to run after the task's main logic
my_custom_task_status = 'completed' # This updates the variable in task data
</spiffworkflow:postScript>
</bpmn:extensionElements>
<!-- ... other task definitions ... -->
</bpmn:task>
Python Scripting Environment¶
Scripts within SpiffWorkflow, whether in <bpmn:script> elements of Script Tasks, or in <spiffworkflow:preScript> and <spiffworkflow:postScript> elements, execute in a specialized Python environment.
Key Characteristics:
No
importStatements: You cannot useimportstatements directly within your scripts.Pre-defined Globals: A set of commonly used modules, functions, and objects are made available globally. This means you can use them directly without importing.
Task Data Context: Scripts operate within the context of the current task’s data. Variables in the task data are directly accessible and can be modified. New variables created in the script are added to the task data.
Available Globals:
The following modules and functions are typically available in the script execution environment.
_strptime: A helper module for parsing dates/times (from Python’s internal_strptime).dateparser: Thedateparserlibrary for parsing dates in various string formats.datetime: Thedatetimemodule from the Python standard library (e.g.,datetime.datetime.now()).decimal: TheDecimaltype from the Python standard librarydecimalmodule for fixed and floating-point arithmetic.json: Thejsonmodule from the Python standard library for working with JSON data (e.g.,json.dumps(),json.loads()).pytz: Thepytzlibrary for working with timezones.time: Thetimemodule from the Python standard library (e.g.,time.time()).timedelta: Thetimedeltaclass from the Python standard librarydatetimemodule.uuid: Theuuidmodule from the Python standard library for generating UUIDs (e.g.,uuid.uuid4()).random: Therandommodule from the Python standard library for generating random numbers.
Built-in Functions and Types: Standard Python built-in functions and types are generally available, such as:
dict,list,set,str,int,float,boolenumerate,filter,format,len,map,min,max,print,range,sum,zip
Notably, print is not available.
Instead, getting information to humans can be handled via instructionsForEndUser in human tasks and manual tasks.
Restricted Environment:
By default, SpiffWorkflow uses RestrictedPython to execute scripts. This means that while many standard library features and built-ins are available, some potentially unsafe operations might be restricted. The safe_globals from RestrictedPython augment the available built-ins.
Custom Helper Functions:
Additionally, helper functions defined in spiffworkflow_backend.services.jinja_service.JinjaHelpers are also exposed globally in the script environment.
This curated environment provides a powerful yet controlled way to implement custom logic within your BPMN processes.
Script Tasks (bpmn:scriptTask)¶
A standard BPMN Script Task executes a script. SpiffWorkflow expects this script to be Python.
Attributes:
scriptFormat:text/x-pythonThe script content is placed directly within the
<bpmn:script>tag.
Execution Context:
The script is executed within the context of the task’s data. Variables are directly accessible and can be modified within the script’s scope. For example, if input_value exists in the task data, you can use it directly and assign new variables like calculated_value = input_value + 5. These new or modified variables become part of the task’s data.
Example:
<bpmn:scriptTask id="MyScriptTask" name="Execute Python Script" scriptFormat="python">
<bpmn:script>
# Access and modify task data
calculated_value = input_value + 5 # Assuming input_value is already in the task's data
</bpmn:script>
</bpmn:scriptTask>
Script Tasks can also utilize spiffworkflow:preScript and spiffworkflow:postScript as described above, which run before and after the main <bpmn:script> respectively.
Service Tasks (bpmn:serviceTask)¶
Service Tasks in SpiffWorkflow are extended to allow for specific operations, most notably calling external HTTP services. This is configured using the spiffworkflow:serviceTaskOperator element within <bpmn:extensionElements>.
Core Extension: spiffworkflow:serviceTaskOperator
This element defines the service operation.
id: (Required) Identifies the operator. For HTTP POST requests, this ishttp/PostRequest. Other operators may exist for different HTTP methods or service types.resultVariable: (Optional) The name of the process variable where the response from the service call will be stored. If not provided, the response isn’t stored in a specific variable. The response stored inresultVariableis a dict that contains the following:body: The response body. Often a Python dictionary/list for JSON responses, otherwise a string.status_code: Integer HTTP status code (e.g.,200,404).
So if your resultVariable is hotResponse, you can access the body with hotResponse["body"] and the status code with hotResponse["status_code"].
Parameters: spiffworkflow:parameters
This container holds all input parameters for the service operator.
spiffworkflow:parameter: Defines each individual parameter.id: (Required) The parameter’s identifier (e.g.,url,headers).type: (Required) The expected data type (e.g.,str,any).anyis often used for dictionaries or complex objects.value: (Required) The parameter’s value.As a variable name: Provide the variable name directly (e.g.,
value="my_url_variable").As a string literal: Enclose in double quotes within the XML attribute (e.g.,
value=""https://api.example.com"").As a JSON string literal (for
type="any"): Provide a JSON formatted string (e.g.,value="{"Content-Type": "application/json"}").
Common Parameters for http/PostRequest:
url(type="str"): (Required) The target URL for the HTTP POST request.<spiffworkflow:parameter id="url" type="str" value=""https://api.example.com/submit"" />
headers(type="any"): (Optional) A dictionary of HTTP headers.<spiffworkflow:parameter id="headers" type="any" value="{"X-API-Key": "secretkey"}" />
data(type="any"): (Optional) The payload/body of the POST request. Can be a dictionary (serialized to JSON ifContent-Typeisapplication/json) or a string.<spiffworkflow:parameter id="data" type="any" value="{"message": "Hello!"}" />
basic_auth_username(type="str"): (Optional) Username for HTTP Basic Authentication.<spiffworkflow:parameter id="basic_auth_username" type="str" value=""my_user"" />
basic_auth_password(type="str"): (Optional) Password for HTTP Basic Authentication. Use SpiffWorkflow’s secret management:"SPIFF_SECRET:your_secret_name".<spiffworkflow:parameter id="basic_auth_password" type="str" value=""SPIFF_SECRET:my_api_secret"" />
Example: HTTP POST Request Service Task
<bpmn:serviceTask id="MyHttpPostTask" name="Send POST Request">
<bpmn:extensionElements>
<spiffworkflow:serviceTaskOperator id="http/PostRequest" resultVariable="post_response">
<spiffworkflow:parameters>
<spiffworkflow:parameter id="url" type="str" value=""https://api.example.com/submit"" />
<spiffworkflow:parameter id="headers" type="any" value="my_headers_variable" /> <!-- my_headers_variable is a dict process variable -->
<spiffworkflow:parameter id="data" type="any" value="request_data_var" /> <!-- request_data_var is a dict process variable -->
<spiffworkflow:parameter id="basic_auth_username" type="str" value=""my_user"" />
<spiffworkflow:parameter id="basic_auth_password" type="str" value=""SPIFF_SECRET:my_api_secret"" />
</spiffworkflow:parameters>
</spiffworkflow:serviceTaskOperator>
<spiffworkflow:postScript>
# Example: Process the result stored in 'post_response'
if post_response and post_response.status_code == 200:
api_call_successful = True
response_body = post_response.body
else:
api_call_successful = False
error_message = f"API call failed with status: {post_response.status_code if post_response else 'N/A'}"
</spiffworkflow:postScript>
</bpmn:extensionElements>
</bpmn:serviceTask>
Important Notes for Service Tasks:
Parameter values: It’s probably better to assign dictionary values for headers and data in script tasks or pre scripts and then referencing these vars rather than using escaped dictionary values inline in the parameter node attribute.
XML Escaping: Ensure special XML characters in literal values are escaped (e.g.,
&becomes&).Secrets: Always use
SPIFF_SECRET:prefix for sensitive string literals or load them securely into variables.Error Handling: Consider BPMN error boundary events to manage exceptions from service calls (e.g., network issues, HTTP 4xx/5xx errors).
Service Tasks can also utilize
spiffworkflow:preScriptandspiffworkflow:postScriptfor logic before and after the main service call.
User Tasks (bpmn:userTask) and Manual Tasks (bpmn:manualTask)¶
spiffworkflow:instructionsForEndUser
For User Tasks and Manual Tasks, this extension allows embedding instructions for the end-user. The content is often a Jinja2 template that can be rendered using the task’s data.
Example:
<bpmn:userTask id="ReviewOrderItem" name="Review Order Item">
<bpmn:extensionElements>
<spiffworkflow:instructionsForEndUser>
Please review the details for product: {{ product_name | default('N/A') }}.
Quantity: {{ quantity | default(1) }}
Notes: {{ customer_notes | default('None') }}
</spiffworkflow:instructionsForEndUser>
</bpmn:extensionElements>
</bpmn:userTask>
spiffworkflow:properties for Form Configuration
User Tasks can be configured to display dynamic forms using JSON Schema for data structure and validation, and optionally a UI Schema for layout and presentation. This is done via spiffworkflow:property elements nested within spiffworkflow:properties.
spiffworkflow:property:name: Specifies the type of schema file. Common values are:formJsonSchemaFilename: The filename of the JSON Schema.formUiSchemaFilename: The filename of the UI Schema (optional).
value: The actual filename (e.g.,my_form_schema.json). These files are typically expected to be co-located with the BPMN process model or in a predefined location accessible to the frontend.
Example:
<bpmn:userTask id="CollectUserData" name="Collect User Data">
<bpmn:extensionElements>
<spiffworkflow:properties>
<spiffworkflow:property name="formJsonSchemaFilename" value="user_data_schema.json" />
<spiffworkflow:property name="formUiSchemaFilename" value="user_data_uischema.json" />
</spiffworkflow:properties>
<spiffworkflow:instructionsForEndUser>
Please fill out the form below.
</spiffworkflow:instructionsForEndUser>
</bpmn:extensionElements>
</bpmn:userTask>
This configuration tells the SpiffWorkflow frontend to render a form based on user_data_schema.json and user_data_uischema.json when this User Task becomes active. The data submitted through this form becomes part of the task’s data.
Before the frontend receives a User Task form, Spiff Arena renders the referenced JSON Schema and UI Schema files through Jinja using the task data for that task. This allows process authors to build dynamic form labels, options, visibility settings, and schema fragments without using custom frontend code.
The final rendered schema must be valid JSON.
Use the tojson filter when injecting strings, lists, dictionaries, booleans, or numbers into JSON Schema or UI Schema files.
Error Definitions (bpmn:error)¶
When defining Error Events, SpiffWorkflow allows specifying a variable name whose value should be captured as part of the error’s payload when the error is thrown.
spiffworkflow:variableName
Used within a <bpmn:error> element’s <bpmn:extensionElements>.
Example:
<bpmn:error id="CustomApplicationError" name="Application Error" errorCode="APP_ERR_101">
<bpmn:extensionElements>
<spiffworkflow:variableName>failed_item_id</spiffworkflow:variableName>
</bpmn:extensionElements>
</bpmn:error>
When an error with this definition is thrown (e.g., from a Script Task or Service Task), the value of the variable failed_item_id from the throwing task’s context can be included in the BpmnEvent payload, making it available to the catching error event.
This document provides an overview of common SpiffWorkflow-specific BPMN extensions. For detailed behavior, advanced configurations, and other extensions, refer to the official SpiffWorkflow documentation and examples.
Condition Expressions¶
Python will be assumed for conditionExpression nodes, and they do not need type or language attributes.
Process Model Directory¶
SpiffWorkflow stores process models in diretories. Each directory contains a bpmn file (or files, in the case of call activities), JSON files representing for schemas and UI schemas, and a process_model.json file with metadata for the process model itself.
Example process_model.json:
{
"description": "This process model defines the how time off request and approval works",
"display_name": "Time Off Request",
"exception_notification_addresses": [],
"fault_or_suspend_on_exception": "fault",
"metadata_extraction_paths": [
{ "key": "time_off_date", "path": "time_off_date" }
],
"primary_file_name": "time_off.bpmn",
"primary_process_id": "time_off"
}
The primary_file_name must be in the same directory, and the primary_process_id must be found in that file, in order for the engine to know where to start execution.
The metadata_extraction_paths should match variables that are generated when the process runs, and these values will be indexed for reporting.