π¦ Generated API β
One file per BPMN process, containing type-safe constants for all extracted elements.
Structure β
The generated Process API is an object (Kotlin) or class (Java) with nested sections:
| Section | Contents |
|---|---|
PROCESS_ID | The process identifier from the BPMN model |
PROCESS_ENGINE | The engine the API was generated for, as a typed BpmnEngine enum (ZEEBE, CAMUNDA_7, OPERATON) |
Elements | All flow node IDs (tasks, events, gateways, subprocesses) |
CallActivities | Called process IDs for call activity elements |
Messages | Message names from message events and receive tasks |
ServiceTasks | Worker types / topics / delegate expressions from service tasks |
Timers | Timer configurations with type and value (BpmnTimer) |
Errors | Error definitions with name and code (BpmnError) |
Compensations | Compensation event IDs |
Signals | Signal names from signal events |
Variables | Per-element variables, split into Inputs / Outputs sub-objects by direction |
Flows | Sequence flows with sourceRef, targetRef, conditionExpression, isDefault |
Relations | Per-element topology: incoming, outgoing, parentId, attachedToRef, attachedElements |
Sections are only included when the BPMN model contains matching elements.
Full Example β
Given a newsletter subscription BPMN process, bpmn-to-code generates:
// Generated by bpmn-to-code
@file:Suppress("unused")
package de.emaarco.example
import io.github.emaarco.bpmn.runtime.BpmnEngine
import io.github.emaarco.bpmn.runtime.BpmnError
import io.github.emaarco.bpmn.runtime.BpmnFlow
import io.github.emaarco.bpmn.runtime.BpmnRelations
import io.github.emaarco.bpmn.runtime.BpmnTimer
import io.github.emaarco.bpmn.runtime.VariableName
object NewsletterSubscriptionProcessApi {
const val PROCESS_ID: String = "newsletterSubscription"
val PROCESS_ENGINE: BpmnEngine = BpmnEngine.ZEEBE
object Elements {
const val ACTIVITY_CONFIRM_REGISTRATION: String = "Activity_ConfirmRegistration"
const val ACTIVITY_SEND_CONFIRMATION_MAIL: String = "Activity_SendConfirmationMail"
const val ACTIVITY_SEND_WELCOME_MAIL: String = "Activity_SendWelcomeMail"
const val CALL_ACTIVITY_ABORT_REGISTRATION: String = "CallActivity_AbortRegistration"
const val END_EVENT_REGISTRATION_COMPLETED: String = "EndEvent_RegistrationCompleted"
const val END_EVENT_SUBSCRIPTION_CONFIRMED: String = "EndEvent_SubscriptionConfirmed"
const val START_EVENT_SUBMIT_REGISTRATION_FORM: String = "StartEvent_SubmitRegistrationForm"
const val SUB_PROCESS_CONFIRMATION: String = "SubProcess_Confirmation"
const val TIMER_EVERY_DAY: String = "Timer_EveryDay"
// ... all other elements
}
object CallActivities {
const val CALL_ACTIVITY_ABORT_REGISTRATION: String = "abort-registration"
}
object Messages {
const val MESSAGE_FORM_SUBMITTED: String = "Message_FormSubmitted"
}
object ServiceTasks {
const val NEWSLETTER_SEND_CONFIRMATION_MAIL: String = "#{newsletterSendConfirmationMail}"
const val NEWSLETTER_SEND_WELCOME_MAIL: String = "\${newsletterSendWelcomeMail}"
const val NEWSLETTER_REGISTRATION_COMPLETED: String = "newsletter.registrationCompleted"
}
object Timers {
val TIMER_EVERY_DAY: BpmnTimer = BpmnTimer("Duration", "PT1M")
val TIMER_AFTER_3_DAYS: BpmnTimer = BpmnTimer("Duration", "\${testVariable}")
}
object Errors {
val ERROR_INVALID_MAIL: BpmnError = BpmnError("Error_InvalidMail", "500")
}
object Compensations {
const val COMPENSATION_END_EVENT_REGISTRATION_ABORTED: String = "CompensationEndEvent_RegistrationAborted"
}
object Signals {
const val SIGNAL_REGISTRATION_NOT_POSSIBLE: String = "Signal_RegistrationNotPossible"
}
object Variables {
object ActivitySendConfirmationMail {
object Inputs {
val SUBSCRIPTION_ID: VariableName = VariableName("subscriptionId")
}
}
object StartEventSubmitRegistrationForm {
object Outputs {
val SUBSCRIPTION_ID: VariableName = VariableName("subscriptionId")
}
}
}
object Flows {
val FLOW_05_I_3_X_1_Y: BpmnFlow = BpmnFlow(
id = "Flow_05i3x1y",
sourceRef = "StartEvent_RequestReceived",
targetRef = "Activity_SendConfirmationMail",
)
// ... all sequence flows
}
object Relations {
val ACTIVITY_CONFIRM_REGISTRATION: BpmnRelations = BpmnRelations(
incoming = listOf("Activity_SendConfirmationMail"),
outgoing = listOf("EndEvent_SubscriptionConfirmed"),
parentId = "SubProcess_Confirmation",
attachedToRef = null,
attachedElements = listOf("Timer_EveryDay"),
)
// ... all elements
}
}// Generated by bpmn-to-code
package de.emaarco.example;
import io.github.emaarco.bpmn.runtime.BpmnEngine;
import io.github.emaarco.bpmn.runtime.BpmnError;
import io.github.emaarco.bpmn.runtime.BpmnFlow;
import io.github.emaarco.bpmn.runtime.BpmnRelations;
import io.github.emaarco.bpmn.runtime.BpmnTimer;
public class NewsletterSubscriptionProcessApi {
public static final String PROCESS_ID = "newsletterSubscription";
public static final BpmnEngine PROCESS_ENGINE = BpmnEngine.ZEEBE;
public static class Elements {
public static final String ACTIVITY_CONFIRM_REGISTRATION = "Activity_ConfirmRegistration";
public static final String ACTIVITY_SEND_WELCOME_MAIL = "Activity_SendWelcomeMail";
// ... same constants as Kotlin, with Java syntax
}
public static class Messages {
public static final String MESSAGE_FORM_SUBMITTED = "Message_FormSubmitted";
}
// ... same structure for ServiceTasks, Timers, Errors, Compensations, Signals, Variables, Flows, Relations
}Flows β
The Flows section contains every sequence flow in the process as a BpmnFlow data class:
data class BpmnFlow(
val id: String,
val sourceRef: String,
val targetRef: String,
val conditionExpression: String? = null,
val isDefault: Boolean = false,
)Use Flows to build process-aware test fixtures or to inspect routing logic in your application code.
Relations β
The Relations section maps every flow node to its topology information via BpmnRelations:
data class BpmnRelations(
val incoming: List<String>, // IDs of incoming sequence flows or elements
val outgoing: List<String>, // IDs of outgoing sequence flows or elements
val parentId: String?, // parent subprocess ID, if any
val attachedToRef: String?, // element this boundary event is attached to
val attachedElements: List<String>, // boundary events attached to this element
)This is useful for traversing process topology in tests, validations, or analysis tools.
Per-Element Variables with Direction β
Every variable is associated with the element that declares it and with the direction in which it flows. The Variables.<Element> object splits its constants into nested Inputs / Outputs sub-objects so consumers can tell whether a variable is read by the element or written by it:
object Variables {
object ActivitySendConfirmationMail {
object Inputs {
val SUBSCRIPTION_ID: VariableName = VariableName("subscriptionId")
}
}
object StartEventSubmitRegistrationForm {
object Outputs {
val SUBSCRIPTION_ID: VariableName = VariableName("subscriptionId")
}
}
}A sub-object is only emitted when it contains at least one variable β one-sided splits (Inputs only or Outputs only) are legal. An element without any directional variables is omitted from Variables entirely.
Variable Extraction β
Variables are extracted from direction-aware BPMN sources. See the engine-specific pages for details:
- Zeebe β
zeebe:input/zeebe:outputinsidezeebe:ioMapping, plusinputElement/inputCollection/outputElement/outputCollectiononzeebe:loopCharacteristics - Camunda 7 β
camunda:inputParameter/camunda:outputParameter,camunda:in/camunda:out, multi-instancecamunda:collection/camunda:elementVariable, and theadditionalInputVariables/additionalOutputVariablesextension properties - Operaton β same patterns as Camunda 7, using the
operaton:namespace
INFO
bpmn-to-code only extracts variables from explicit BPMN definitions. Variables only referenced in expressions (sequence flows, gateway conditions, script tasks) are intentionally ignored. This is by design β the BPMN model should be the single source of truth for its variable contract.
Model Merging β
When multiple BPMN files share the same processId, bpmn-to-code merges them into a single API. The generated API contains the superset of all elements across all files.
This is useful for process variants (e.g. dev vs prod configurations) that share the same process identifier. If multiple files define the same processId, use the variantName BPMN extension property to distinguish them and prevent silent overwrites in the JSON output.