A workflow is an array of WorkflowTransitionDto objects forming a state machine.
| Field |
Type |
Required |
Description |
startState |
string |
Yes |
Current state (UPPER_SNAKE_CASE) |
event |
string |
Yes |
Trigger event (UPPER_SNAKE_CASE verb) |
endStateOne |
WorkflowEndStateDto |
Yes |
Primary destination state |
endStateTwo |
WorkflowEndStateDto |
No |
Alternate destination (conditional) |
endStateCondition |
WorkflowEndStateConditionDto |
No |
Condition for choosing endStateTwo |
authorisation |
WorkflowAuthorisationDto |
Yes |
Who can trigger this event |
position |
{x, y} |
No |
Visual editor position |
eventNames |
Map<string, string> |
No |
Localized event names (en, fr, rw) |
eventConfigs |
WorkflowEventConfigsDto |
No |
Event configuration |
| Field |
Type |
Required |
Description |
stateCode |
string |
Yes |
UPPER_SNAKE_CASE state identifier |
stateName |
string |
Yes |
Default display name |
stateNames |
Map<string, string> |
No |
Localized names {en, fr, rw} |
breakingAction |
WorkflowActionDto |
No |
Single breaking action (legacy, prefer breakingActions) |
breakingActions |
WorkflowActionDto[] |
No |
Breaking actions (rejection/cancellation notifications) |
nonBreakingActions |
WorkflowActionDto[] |
No |
Non-breaking actions (certificates, integrations) |
nextEvent |
string |
No |
Auto-trigger next event after reaching this state |
position |
{x, y} |
No |
Visual editor position |
| Field |
Type |
Required |
Description |
actionType |
string |
Yes |
Must be a valid EWorkflowActionType value |
args |
WorkflowArgumentsDto |
No |
Action-specific arguments |
| Action Type |
Purpose |
NOTIFICATION |
Send notification (email/SMS) to applicant |
INTERNAL_NOTIFICATION |
Send notification to internal staff |
FORM_FIELD_NOTIFICATION |
Send notification to address from a form field |
OFFICE_ASSIGNMENT_NOTIFICATION |
Notify assigned office |
INTEGRATION |
Call external integration endpoint |
INTEGRATION_UPDATE |
Update via integration |
GENERATE_CERTIFICATE |
Generate output document/certificate |
BILL_ID_GENERATION |
Generate payment bill ID |
MASTER_BILL_ID_GENERATION |
Generate master bill ID |
SERVICE_CENTER_MASTER_BILL_ID_GENERATION |
Service center master bill |
EXECUTE_PRICING |
Execute pricing calculation |
OFFICE_ASSIGNMENT |
Assign application to office |
FETCH_ID_PHOTO |
Fetch ID photo from registry |
SLOT_BOOKING |
Book appointment slot |
REDEEM_SLOT |
Redeem a booked slot |
SLOT_REBOOKING |
Rebook an appointment slot |
APPLY_SERVICE |
Apply for a related service |
APPLY_NORMAL_SERVICE |
Apply for a normal service |
TRIGGER_PARENT |
Trigger parent service event |
TRIGGER_PARENT_SERVICE_EVENT |
Trigger specific parent event |
TRIGGER_CHILD_SERVICE_EVENT |
Trigger child service event |
CREATE_CHILD_APPLICATION |
Create a child application |
APPLY_OR_RESUBMIT_CHILD_APPLICATION |
Apply or resubmit child |
UPDATE_DATASET |
Update a dataset |
SYSTEM_REQUEST_FOR_ACTION |
System-generated RFA |
URL_CALL |
Call an external URL |
VALIDATE_UNIQUE_APPLICATION_PROPERTY |
Validate unique property |
notificationType: DYNAMIC | STATIC
dynamicTemplate: { "en": { "templateCode": "CODE" } }
staticTemplate: { "en": { "templateCode": "CODE" } }
attachment: array of attachment configs
internalEmailNotificationRecipients: string[]
internalSmsNotificationRecipients: string[]
formFieldsSmsNotificationRecipients: string[]
formFieldsEmailNotificationRecipients: string[]
endpointCode: string
async: boolean
certificateName: string
certificateNames: { "en": "Name" }
certificateTemplateCode: { "en": "TEMPLATE_CODE" }
certificateExpirationDays: number
certificateWithList: boolean
mergeDocuments: string[]
invoiceDescription: string
currency: string
officeAssignmentType: string
officeLevel: string
formFieldKey: string
formFieldKeys: string[]
fixedOfficeCode: string
officeAssignmentExpression: string
officeId: string
groupIdField: string
timeRangeIdField: string
seatsField: string
fieldNames: string[]
feedbackSection: string
feedbackReason: string
comment: string
serviceCode: string
applyPayload: { "sourceField": "targetField" }
event: string
parametersToRetain: { "key": "value" }
datasetCode: string
datasetUpdateType: string
datasetFieldsMapping: { "formField": "datasetField" }
propertyKey: string
propertyValuePath: string
applicationStates: string[]
errorMessageCode: string
excludeCurrentApplication: boolean (default true)
source: string
sourceDocumentType: string
sourceDocumentCode: string
| Field |
Type |
Description |
authorisedRoles |
string[] |
Role codes (e.g., REVIEWER, APPROVER) |
authorisedUsers |
UUID[] |
Specific user IDs |
systemAuthorised |
boolean |
System-triggered (no human actor) |
At least one of authorisedRoles, authorisedUsers, or systemAuthorised must be set.
| Field |
Type |
Description |
endStateTwo |
WorkflowSingleEndStateConditionDto[] |
Conditions for routing to endStateTwo |
validationStrategy |
string |
Strategy identifier |
validationStrategyIdentifierField |
string |
Field for strategy lookup |
childReadyStates |
string[] |
Child states to wait for |
duplicateCheckApplicationStates |
string[] |
States to check for duplicates |
| Field |
Type |
Required |
Description |
parameter |
string |
Yes |
Form field key to evaluate |
value |
string |
Yes |
Expected value |
comparator |
EFieldComparator |
Yes |
GT, LT, EQ, GTE, LTE, NEQ, CONTAINS |
| Field |
Type |
Description |
eventDueDate |
string |
Due date expression |
- Exactly one initial state (typically
SUBMITTED)
- Every state must be reachable from the initial state
- Terminal states have no outgoing transitions
- State names:
UPPER_SNAKE_CASE nouns (SUBMITTED, APPROVED, REJECTED)
- Event names:
UPPER_SNAKE_CASE verbs (APPROVE, REJECT, FORWARD)
- Breaking actions = failure path (rejection, cancellation)
- Non-breaking actions = success path (certificates, integrations, notifications)
[
{
"startState": "SUBMITTED",
"event": "APPROVE",
"endStateOne": {
"stateCode": "APPROVED",
"stateName": "Approved",
"stateNames": { "en": "Approved", "rw": "Byemejwe" },
"breakingAction": null,
"breakingActions": [],
"nonBreakingActions": [
{
"actionType": "NOTIFICATION",
"args": {
"notificationType": "DYNAMIC",
"dynamicTemplate": {
"en": { "templateCode": "TODO: approval_template_en" }
}
}
},
{
"actionType": "GENERATE_CERTIFICATE",
"args": {
"certificateName": "Certificate",
"certificateTemplateCode": { "en": "TODO: cert_template_code" }
}
}
],
"nextEvent": null,
"position": { "x": 400, "y": 100 }
},
"endStateTwo": null,
"endStateCondition": null,
"authorisation": {
"authorisedRoles": ["REVIEWER"],
"authorisedUsers": null,
"systemAuthorised": null
},
"position": { "x": 200, "y": 100 },
"eventNames": { "en": "Approve", "rw": "Emeza" },
"eventConfigs": null
},
{
"startState": "SUBMITTED",
"event": "REJECT",
"endStateOne": {
"stateCode": "REJECTED",
"stateName": "Rejected",
"stateNames": { "en": "Rejected", "rw": "Byanzwe" },
"breakingAction": null,
"breakingActions": [
{
"actionType": "NOTIFICATION",
"args": {
"notificationType": "DYNAMIC",
"dynamicTemplate": {
"en": { "templateCode": "TODO: rejection_template_en" }
}
}
}
],
"nonBreakingActions": [],
"nextEvent": null,
"position": { "x": 400, "y": 300 }
},
"endStateTwo": null,
"endStateCondition": null,
"authorisation": {
"authorisedRoles": ["REVIEWER"],
"authorisedUsers": null,
"systemAuthorised": null
},
"position": { "x": 200, "y": 300 },
"eventNames": { "en": "Reject", "rw": "Hagarika" },
"eventConfigs": null
}
]