Workflow Actions

Every WorkflowAction type, its arguments, and how they fire on entry or exit of a state.

Reference for all EActionType values. Each action goes in either breakingAction (singular), breakingActions (plural array — used when EXECUTE_PRICING + BILL_ID_GENERATION must fire together), or nonBreakingActions (array).


NOTIFICATION

Sends email and/or SMS to the applicant. Goes in nonBreakingActions.

EApplicationContext: APPLICATION, APPLICATION_DETAILS, PAYMENT_TRANSACTION, APPLICATION_CERTIFICATES

Two subtypes based on notificationType:

STATIC — fixed HTML/SMS content

Use when message content is a fixed layout authored inline.

Locale keys: en, fr, rw

Args:

FieldTypeRequiredNotes
notificationType"STATIC"Yes
staticTemplateobjectYesKeys: en, fr, rw
staticTemplate[locale].smsBodystringYesPlain text SMS
staticTemplate[locale].subjectstringYesEmail subject
staticTemplate[locale].emailBodystringYesHTML email body
staticTemplate[locale].titlestring/nullNoDefaults to null
staticTemplate[locale].emailMessagestring/nullNoDefaults to null
dynamicTemplatenullYesMust be null for STATIC
certificateWithListbooleanNoDefault: false
updateFieldWithDatasetValuebooleanNoDefault: false
excludeCurrentApplicationbooleanNoDefault: true
attachmentarray/nullNoAttach files to the email — see Attachments section below

Example:

{
  "actionType": "NOTIFICATION",
  "args": {
    "notificationType": "STATIC",
    "staticTemplate": {
      "en": {
        "title": null,
        "subject": "Application Status",
        "emailBody": "<p>Dear customer, your application has been received.</p>",
        "smsBody": "Your application has been received.",
        "emailMessage": null
      },
      "fr": {
        "title": null,
        "subject": "Statut de la demande",
        "emailBody": "<p>Cher client, votre demande a été reçue.</p>",
        "smsBody": "Votre demande a été reçue.",
        "emailMessage": null
      },
      "rw": {
        "title": null,
        "subject": "Aho ubusabe bugeze",
        "emailBody": "<p>Mukiriya wacu, ubusabe bwawe bwakiriwe.</p>",
        "smsBody": "Ubusabe bwawe bwakiriwe.",
        "emailMessage": null
      }
    },
    "dynamicTemplate": null,
    "certificateWithList": false,
    "updateFieldWithDatasetValue": false,
    "excludeCurrentApplication": true
  }
}

Attachments

Optional attachment array on any NOTIFICATION. Two source types:

DOCUMENT_GENERATION_ENGINE — certificate generated on IremboHub

Use when the certificate is generated internally by IremboHub's document generation engine. attachmentFields must be null.

"attachment": [
  {
    "sourceType": "DOCUMENT_GENERATION_ENGINE",
    "sourceUrl": null,
    "attachmentFields": null
  }
]

EXTERNAL_SOURCE — certificate or file fetched from an external API

Use when the attachment comes from an external integration endpoint.

How to find the correct attachmentFields:

  1. Call get_integration_endpoint_mapping with the endpoint code.
  2. Look at response.transform — it maps output field names to expressions.
  3. The attachmentFields are the transform keys whose values become the MinIO object name used to retrieve the file later. These keys contain fileName in their name.
  4. Do NOT include keys ending in -file-N — those hold base64 content, not file names.

Example — PRIME_CONTRACT_PUSH response.transform:

Transform keyValueUse as attachmentField?
CERTIFICATE_FILE-file-1"data:application/pdf;base64,"& DocumentNo — base64 content
CERTIFICATE_FILE-fileName-1originalPayload.serviceName & "- Contract - " & ...Maybe — a file name
CONTRACT-Certificate-fileNameoriginalPayload.serviceName & "- Contract - " & ... & ".pdf"Yes — this is the MinIO lookup key

attachmentFields: ["CONTRACT-Certificate-fileName"]

Rule: pick transform keys that contain fileName and whose value produces a .pdf / document name. This is the name the system uses to fetch the file from MinIO when sending the notification.

"attachment": [
  {
    "sourceType": "EXTERNAL_SOURCE",
    "sourceUrl": null,
    "attachmentFields": [
      "CONTRACT-Certificate-fileName",
      "EBM-Certificate-fileName",
      "CERTIFICATE-Certificate-fileName"
    ]
  }
]

NOTIFICATION_ENGINE_TEMPLATE — pre-built templates from notification engine

Use when the notification template already exists in the notification engine.

Locale keys: en, fr, rw

Args:

FieldTypeRequiredNotes
notificationType"NOTIFICATION_ENGINE_TEMPLATE"Yes
dynamicTemplateobjectYesKeys: en, fr, rw
dynamicTemplate[locale].smsTemplateNamestringYesTemplate code for SMS
dynamicTemplate[locale].emailTemplateNamestringYesTemplate code for email
staticTemplatenullYesMust be null
attachmentarray/nullNoAttach files to the email — see Attachments section above

Example:

{
  "actionType": "NOTIFICATION",
  "args": {
    "notificationType": "NOTIFICATION_ENGINE_TEMPLATE",
    "staticTemplate": null,
    "dynamicTemplate": {
      "en": {
        "smsTemplateName": "PAYMENT_PENDING_SMS_EN",
        "emailTemplateName": "PAYMENT_PENDING_EMAIL_EN"
      },
      "fr": {
        "smsTemplateName": "PAYMENT_PENDING_SMS_FR",
        "emailTemplateName": "PAYMENT_PENDING_EMAIL_FR"
      },
      "rw": {
        "smsTemplateName": "PAYMENT_PENDING_SMS_RW",
        "emailTemplateName": "PAYMENT_PENDING_EMAIL_RW"
      }
    }
  }
}

GENERATE_CERTIFICATE

Generates a certificate for the application. Goes in breakingAction (singular).

EApplicationContext: APPLICATION, APPLICATION_DETAILS, APPLICATION_STATE_TRACKER, PAYMENT_TRANSACTION, APPLICATION_FEEDBACK, APPLICATION_CERTIFICATES

Two cases depending on where the certificate is created:

Internal — certificate template created on IremboHub

Use when the certificate template exists in IremboHub's document generation engine.

To author the template file, use generate_certificate_template — it generates a complete, print-ready FreeMarker HTML file from the service's form fields. Upload the resulting .html file in the Service Management Portal before calling list_certificate_templates. See Template Authoring for the full variable reference.

Always call list_certificate_templates(organizationId) first to look up the correct code values — never hardcode them. Each template object has:

  • code → the value to use in certificateTemplateCode
  • templateLanguage (en-US, fr-FR, rw-RW) → maps to the short locale key (en, fr, rw) certificateTemplateCode is keyed by

Args (GenerateCertificateArgs):

FieldTypeRequiredNotes
certificateNamestringYesInternal name for the certificate, e.g. "Driver_License"
certificateTemplateCodeobjectYesLocale keys: en, fr, rw → values are code from list_certificate_templates
certificateExpirationDaysnumber/nullNoDays until certificate expires, e.g. 1825 (5 years). Null if no expiry
reminderOffsetDaysnumber/nullNoDays before expiry to send a reminder notification. Null if no reminder needed
reminderNotificationsobject/nullNoNotification templates for the reminder (transient — not persisted). Same locale structure as STATIC notification
imagesFromTheFormobject/nullNoMap of image placeholder name → form field key. Used to embed form-uploaded images into the certificate template, e.g. {"photo": "FIELD_PHOTO"}
mergeDocumentsarray/nullNoList of document field keys to merge into the certificate PDF (e.g. attachments appended to the certificate)
certificateWithListbooleanNoDefault: false

Example (with all optional fields):

{
  "actionType": "GENERATE_CERTIFICATE",
  "args": {
    "certificateName": "Driver_License",
    "certificateTemplateCode": {
      "en": "DL_TEMPLATE_EN",
      "fr": "DL_TEMPLATE_FR",
      "rw": "DL_TEMPLATE_RW"
    },
    "certificateExpirationDays": 1825,
    "reminderOffsetDays": 30,
    "imagesFromTheForm": {
      "applicantPhoto": "FIELD_PASSPORT_PHOTO"
    },
    "mergeDocuments": ["FIELD_SUPPORTING_DOCUMENT"],
    "certificateWithList": false
  }
}

External — certificate fetched from an external API

No GENERATE_CERTIFICATE action is used. Instead, attach the certificate to an approval NOTIFICATION using attachment with sourceType: "EXTERNAL_SOURCE". The attachmentFields come from the integration endpoint's response.transform — pick the keys that contain fileName (these are the MinIO object name keys). Call get_integration_endpoint_mapping with the endpoint code to read the transform and identify them. Do not use keys ending in -file-N (those are base64 content).

{
  "actionType": "NOTIFICATION",
  "args": {
    "notificationType": "STATIC",
    "staticTemplate": { "...": "..." },
    "dynamicTemplate": null,
    "attachment": [
      {
        "sourceType": "EXTERNAL_SOURCE",
        "sourceUrl": null,
        "attachmentFields": ["CONTRACT-Certificate-fileName", "CERTIFICATE-Certificate-fileName"]
      }
    ]
  }
}

INTEGRATION

EApplicationContext: APPLICATION, APPLICATION_DETAILS, PAYMENT_TRANSACTION, APPLICATION_FEEDBACK

Triggered during workflow transitions to send application data to a partner system — such as RDB, banks, or other government agencies. Can send application details, notifications, or completed certificates to external systems.

Always system-triggered (systemAuthorised: true, authorisedRoles: null).

Placement:

  • breakingAction (singular) — use when the partner system's acknowledgment is critical and the workflow must wait for a successful push before continuing
  • nonBreakingActions (array) — use for optional updates or notifications that should not block the workflow

Dependencies:

  • The partner endpoint must be active and properly configured on the integration portal
  • Required application data fields must be present — missing fields may cause failures

Args:

FieldTypeRequiredNotes
endpointCodestringYesCode of the endpoint configured on the integration portal. Each endpoint holds the API mapping to the external partner system. Ask the user for the code — flag as TODO: describe endpoint if unknown
asyncbooleanYestrue = workflow continues immediately without waiting for partner response; partner calls back via PUSH event. false = workflow waits until partner confirms receipt
certificateWithListbooleanNoDefault: false
updateFieldWithDatasetValuebooleanNoDefault: false
excludeCurrentApplicationbooleanNoDefault: true

async: true — partner callback pattern

When async: true, the integration requires two transitions:

  1. INITIATE_PUSH (system) → PENDING_* state with INTEGRATION in breakingAction, nextEvent: null
  2. PUSH (system/partner callback) → next state with nextEvent: "INITIATE_PUSH" to continue the chain
{
  "startState": "PENDING_EBM_FETCH",
  "event": "INITIATE_PUSH",
  "endStateOne": {
    "stateName": "GENERATED_EBM",
    "stateNames": null,
    "stateCode": "GENERATED_EBM",
    "breakingAction": {
      "actionType": "INTEGRATION",
      "args": {
        "endpointCode": "PRIME_EBM_PUSH",
        "async": true,
        "certificateWithList": false,
        "updateFieldWithDatasetValue": false,
        "excludeCurrentApplication": true
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": { "authorisedRoles": null, "authorisedUsers": null, "systemAuthorised": true },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

The PUSH callback transition that follows:

{
  "startState": "GENERATED_EBM",
  "event": "PUSH",
  "endStateOne": {
    "stateName": "Pending approval",
    "stateNames": null,
    "stateCode": "PENDING_APPROVAL",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "TRANSITION",
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": { "authorisedRoles": null, "authorisedUsers": null, "systemAuthorised": true },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

nextEvent on the PUSH callback depends on what comes next:

What followsnextEvent value
Another INTEGRATION in the chain"INITIATE_PUSH"
Officer review or other state machine step"TRANSITION"
Final state / no auto-advancenull

async: false — synchronous

No PUSH transition needed. The integration call completes inline and the workflow auto-advances via nextEvent on endStateOne. Chain multiple sync integrations by setting nextEvent: "INITIATE_PUSH" on each — the next INITIATE_PUSH fires automatically after the sync call succeeds.

authorisation is null (not the full object) for these system-driven transitions.

Example — two chained sync integrations:

{
  "startState": "SUBMITTED",
  "event": "INITIATE_PUSH",
  "endStateOne": {
    "stateName": "Pending Push to Partner",
    "stateNames": null,
    "stateCode": "PUSHED_TO_PARTNER",
    "breakingAction": {
      "actionType": "INTEGRATION",
      "args": {
        "endpointCode": "RDB_MASS_ID_DBR_PUSH_2",
        "async": false,
        "certificateWithList": false,
        "updateFieldWithDatasetValue": false
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "INITIATE_PUSH",
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": null,
  "position": null,
  "eventNames": null,
  "eventConfigs": null
},
{
  "startState": "PUSHED_TO_PARTNER",
  "event": "INITIATE_PUSH",
  "endStateOne": {
    "stateName": "Pending Push to Partner",
    "stateNames": null,
    "stateCode": "PUSHED_TO_SAVE_NAVIGATION_CREATION",
    "breakingAction": {
      "actionType": "INTEGRATION",
      "args": {
        "endpointCode": "RDB_CREATE_NAVIGATION_IDS_DBR_PUSH",
        "async": false,
        "certificateWithList": false,
        "updateFieldWithDatasetValue": false
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "INITIATE_PUSH",
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": null,
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

nextEvent on sync integrations:

What followsnextEvent value
Another sync integration in the chain"INITIATE_PUSH"
Officer review or other state machine step"TRANSITION"
Final state / no auto-advancenull

INTEGRATION_UPDATE — retain values from integration response

Use when the integration response contains values that need to be mapped back into the application (e.g. a calculated price, an applicant name, or a reference number from an external system).

Same actionType: "INTEGRATION" — distinguished by the presence of parametersToRetain.

parametersToRetain — object where:

  • key = field path in the integration response
  • value = application field to write the value into

Supports nested paths, e.g. "data.amount" or "data.details[0].amount".

Placement: breakingAction or nonBreakingActions — use breaking if the workflow depends on the retained values for further transitions.

{
  "actionType": "INTEGRATION",
  "args": {
    "endpointCode": "CODE",
    "async": false,
    "parametersToRetain": {
      "amount": "appPrice",
      "name": "applicantName"
    }
  }
}

Chaining multiple integrations

Each integration is its own dedicated transition — never stack multiple INTEGRATION actions in one state. Chain them by setting nextEvent: "INITIATE_PUSH" on the PUSH callback's endStateOne when the next step is another integration. See Workflow Patterns for the integration-chain pattern.


BILL_ID_GENERATION

EApplicationContext: APPLICATION, PAYMENT_TRANSACTION

Generates a unique Bill ID for the application so the applicant can proceed with payment.

Triggered: system — systemAuthorised: true, authorisedRoles: null

Two placement modes depending on pricing scenario:

ScenarioPlacement
Price is fixed/knownbreakingAction (singular) alone, args: null — no EXECUTE_PRICING needed
Price starts at 0 and will be determined later (e.g. after officer review)breakingActions (plural array) paired with EXECUTE_PRICING at the transition where the price is finally set

The generated Bill ID is available as ${billId} in subsequent notification templates.

{
  "actionType": "BILL_ID_GENERATION",
  "args": null
}

Optional arg — invoiceDescription:

A FreeMarker template string rendered against the application payload and used as the payment invoice description. Pass as a key in args (not a typed DTO field — read directly from the args map):

{
  "actionType": "BILL_ID_GENERATION",
  "args": {
    "invoiceDescription": "Payment for ${serviceName} - Application ${applicationNumber}"
  }
}

Always configure as breaking — the workflow cannot proceed without a successfully generated Bill ID. Optionally pair with a NOTIFICATION (nonBreakingActions) to inform the applicant of the generated bill.

This action only produces a unique payment identifier. It does not generate a certificate.

Full example — APPLY with two paths (paid vs free):

endStateOne = payment path (BILL_ID_GENERATION fires, applicant waits for PAY) endStateTwo = free path (no bill, auto-advances to ASSIGN_OFFICE)

{
  "startState": "NEW",
  "event": "APPLY",
  "endStateOne": {
    "stateName": "Payment Pending",
    "stateNames": null,
    "stateCode": "PAYMENT_PENDING",
    "breakingAction": {
      "actionType": "BILL_ID_GENERATION",
      "args": null
    },
    "breakingActions": null,
    "nonBreakingActions": [
      {
        "actionType": "NOTIFICATION",
        "args": {
          "notificationType": "NOTIFICATION_ENGINE_TEMPLATE",
          "staticTemplate": null,
          "dynamicTemplate": {
            "en": {
              "smsTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING",
              "emailTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING"
            },
            "fr": {
              "smsTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING",
              "emailTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING"
            },
            "rw": {
              "smsTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING",
              "emailTemplateName": "SVC_SUBMITTED_PAYMENT_PENDING"
            }
          },
          "certificateWithList": false
        }
      }
    ],
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": {
    "stateName": "Submitted",
    "stateNames": null,
    "stateCode": "SUBMITTED",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": [
      {
        "actionType": "NOTIFICATION",
        "args": {
          "notificationType": "NOTIFICATION_ENGINE_TEMPLATE",
          "staticTemplate": null,
          "dynamicTemplate": {
            "en": {
              "smsTemplateName": "SVC_SUBMITTED_FREE",
              "emailTemplateName": "SVC_SUBMITTED_FREE"
            },
            "fr": {
              "smsTemplateName": "SVC_SUBMITTED_FREE",
              "emailTemplateName": "SVC_SUBMITTED_FREE"
            },
            "rw": {
              "smsTemplateName": "SVC_SUBMITTED_FREE",
              "emailTemplateName": "SVC_SUBMITTED_FREE"
            }
          },
          "certificateWithList": false
        }
      }
    ],
    "nextEvent": "ASSIGN_OFFICE",
    "position": null
  },
  "endStateCondition": null,
  "authorisation": null,
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

EXECUTE_PRICING

EApplicationContext: APPLICATION, APPLICATION_DETAILS, PAYMENT_TRANSACTION, APPLICATION_FEEDBACK

Calculates the price for the application based on the configured pricing rules. Always paired with BILL_ID_GENERATION in breakingActions (plural array). args must always be null.

Placement: breakingActions (plural array) — always together with BILL_ID_GENERATION

Triggered: system — systemAuthorised: true, authorisedRoles: null

When to use:

  • Price starts at 0 and is determined later (e.g. after officer review or integration) — place EXECUTE_PRICING + BILL_ID_GENERATION together in breakingActions at the transition where the price is finally set
  • Price is fixed/known — use BILL_ID_GENERATION alone in breakingAction (singular); no EXECUTE_PRICING needed
{
  "actionType": "EXECUTE_PRICING",
  "args": null
}

Standard PAYMENT_PENDING endState with both actions:

{
  "stateName": "Payment Pending",
  "stateNames": null,
  "stateCode": "PAYMENT_PENDING",
  "breakingAction": null,
  "breakingActions": [
    { "actionType": "EXECUTE_PRICING", "args": null },
    { "actionType": "BILL_ID_GENERATION", "args": null }
  ],
  "nonBreakingActions": [
    { "actionType": "NOTIFICATION", "args": { "...payment pending notification..." } }
  ],
  "nextEvent": null,
  "position": null
}

OFFICE_ASSIGNMENT

EApplicationContext: APPLICATION, APPLICATION_DETAILS

Assigns the application to an office so it lands in the correct officer's queue. Always a breakingAction (singular). Always system-triggered (systemAuthorised: true, authorisedRoles: null).

Full args structure:

FieldTypeNotes
officeAssignmentTypestringSee subtypes below
officeLevelstringLEVEL_1, LEVEL_2, LEVEL_3, etc.
officeIdstring/nullUUID — only for CODE_FIXED_OFFICE. Call list_offices to get it — never hardcode
formFieldKeystring/nullForm field key — required for location/field-based types
formFieldKeysarray/nullMultiple field keys — used by CODE_FROM_LOCATION when matching on several fields
fixedOfficeCodenullNot used — always null
officeAssignmentExpressionnullNot used — always null

CODE_FIXED_OFFICE

Assign to the same office for every application. Call list_offices(organisationId, level) to look up officeId — never hardcode UUIDs.

{
  "actionType": "OFFICE_ASSIGNMENT",
  "args": {
    "officeId": "0c9984f2-9708-4566-8bb2-9a7c48b431a9",
    "officeLevel": "LEVEL_1",
    "formFieldKey": null,
    "formFieldKeys": null,
    "fixedOfficeCode": null,
    "officeAssignmentType": "CODE_FIXED_OFFICE",
    "officeAssignmentExpression": null
  }
}

CODE_FROM_LOCATION

Assign based on a location field the applicant selected. formFieldKey must end with .id. Use formFieldKeys (array) when matching on multiple location fields.

{
  "actionType": "OFFICE_ASSIGNMENT",
  "args": {
    "officeId": null,
    "officeLevel": "LEVEL_1",
    "formFieldKey": "processingSector.id",
    "formFieldKeys": ["processingSector.id", "embassy.id"],
    "fixedOfficeCode": null,
    "officeAssignmentType": "CODE_FROM_LOCATION",
    "officeAssignmentExpression": null
  }
}

CODE_IN_APPLICATION_OFFICE

Assign based on the .value of an office field on the form (stores the office value directly).

{
  "actionType": "OFFICE_ASSIGNMENT",
  "args": {
    "officeId": null,
    "officeLevel": "LEVEL_1",
    "formFieldKey": "applicationOffice.value",
    "formFieldKeys": null,
    "fixedOfficeCode": null,
    "officeAssignmentType": "CODE_IN_APPLICATION_OFFICE",
    "officeAssignmentExpression": null
  }
}

CODE_APPLICATION_FIELD_CODE

Assign based on the office code stored in a form field.

{
  "actionType": "OFFICE_ASSIGNMENT",
  "args": {
    "officeId": null,
    "officeLevel": "LEVEL_1",
    "formFieldKey": "applicationOffice.value",
    "formFieldKeys": null,
    "fixedOfficeCode": null,
    "officeAssignmentType": "CODE_APPLICATION_FIELD_CODE",
    "officeAssignmentExpression": null
  }
}

CODE_FROM_AN_EXPRESSION exists in the enum but is not yet implemented — do not use.


OFFICE_ASSIGNMENT_NOTIFICATION

EApplicationContext: APPLICATION, APPLICATION_DETAILS, PAYMENT_TRANSACTION, APPLICATION_CERTIFICATES

Notifies the assigned officer that a new application has landed in their queue. Always paired with OFFICE_ASSIGNMENT — goes in nonBreakingActions on the same endState.

Rule: The officeAssignmentType, officeLevel, formFieldKey, and officeId in OFFICE_ASSIGNMENT_NOTIFICATION must match exactly what is in the paired OFFICE_ASSIGNMENT breakingAction.

Locale keys: en, fr, rw (same as STATIC notification)

Args:

FieldTypeNotes
notificationType"STATIC"Always STATIC for officer notifications
staticTemplateobjectKeys: en, fr, rw — notification sent to the officer
officeAssignmentTypestringMust match the paired OFFICE_ASSIGNMENT
officeLevelstringMust match the paired OFFICE_ASSIGNMENT
formFieldKeystring/nullMust match the paired OFFICE_ASSIGNMENT
officeIdstring/nullMust match the paired OFFICE_ASSIGNMENT
certificateWithListbooleanDefault: false
updateFieldWithDatasetValuebooleanDefault: false
excludeCurrentApplicationbooleanDefault: true

Example (paired with CODE_FROM_LOCATION LEVEL_1):

{
  "actionType": "OFFICE_ASSIGNMENT_NOTIFICATION",
  "args": {
    "notificationType": "STATIC",
    "staticTemplate": {
      "en": {
        "title": "Application Submitted",
        "subject": "Application Submitted",
        "emailBody": "<p>Dear Officer,<br/><br/>You have received an application for ${serviceName} with application number ${applicationNumber}, and it is pending processing!</p>",
        "smsBody": "Dear Officer, you have received an application for ${serviceName} with application number ${applicationNumber}, pending processing.",
        "emailMessage": null
      },
      "fr": {
        "title": "Demande Reçue",
        "subject": "Demande Reçue",
        "emailBody": "<p>Cher Officer,<br/><br/>Vous avez reçu une demande pour ${serviceName} avec le numéro ${applicationNumber}, en attente de traitement!</p>",
        "smsBody": "Cher Officer, vous avez reçu une demande pour ${serviceName} avec le numéro ${applicationNumber}, en attente de traitement.",
        "emailMessage": null
      },
      "rw": {
        "title": "Dosiye Yakiriwe",
        "subject": "Dosiye Yakiriwe",
        "emailBody": "<p>Kuri Officer,<br/><br/>Wakiriye dosiye isaba ${serviceName} ifite nomero ${applicationNumber}, itegereje gutunganywa!</p>",
        "smsBody": "Kuri Officer, wakiriye dosiye isaba ${serviceName} ifite nomero ${applicationNumber}, itegereje gutunganywa.",
        "emailMessage": null
      }
    },
    "officeAssignmentType": "CODE_FROM_LOCATION",
    "formFieldKey": "FIELD_CORRECTIONAL_FACILITY_NAME_DISTRICT.id",
    "officeLevel": "LEVEL_1",
    "officeId": null,
    "certificateWithList": false,
    "updateFieldWithDatasetValue": false,
    "excludeCurrentApplication": true
  }
}

When advancing to LEVEL_2, repeat with "officeLevel": "LEVEL_2" matching the new OFFICE_ASSIGNMENT.


REDEEM_SLOT

EApplicationContext: APPLICATION, APPLICATION_DETAILS

Releases a previously reserved slot back to available inventory by adding seats back to the slot's capacity. Used on rejection transitions in services with slot booking to ensure inventory remains accurate when an application is rejected.

Not a confirmation — it is a reversal. REDEEM_SLOT is the opposite of SLOT_BOOKING:

  • SLOT_BOOKING (on APPLY) — subtracts seats from inventory (reserves them)
  • REDEEM_SLOT (on REJECT) — adds seats back to inventory (releases them)

Placement: breakingAction (singular) — always breaking. Place on every REJECT endState when the service uses slot booking.

Args:

FieldTypeNotes
groupIdFieldstringForm field path holding the Group ID selected by the user
timeRangeIdFieldstringForm field path holding the Time Range ID selected by the user
seatsFieldstringForm field path holding the number of seats to release

Example — on a REJECT transition:

{
  "startState": "PAID",
  "event": "REJECT",
  "endStateOne": {
    "stateName": "Rejected",
    "stateNames": null,
    "stateCode": "REJECTED",
    "breakingAction": {
      "actionType": "REDEEM_SLOT",
      "args": {
        "groupIdField": "slotbooking.group.id",
        "timeRangeIdField": "slotbooking.timeRange.id",
        "seatsField": "slotbooking.seats"
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": {
    "authorisedRoles": ["ROLE_OFFICER"],
    "authorisedUsers": null,
    "systemAuthorised": false
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

Dependencies:

  • SLOT_BOOKING must have run on the APPLY transition (seats must have been subtracted first)
  • Field paths must match the form structure used in SLOT_BOOKING

SLOT_REBOOKING

EApplicationContext: APPLICATION, APPLICATION_DETAILS

Updates the slot booking when an applicant changes their slot selection after a REQUEST_FOR_ACTION. Calls updateSlotsSeats() — replaces the previously reserved seat count with the new one from the resubmitted form. Used on the SUBMIT_RFA event transition.

Placement: breakingAction (singular) — always breaking. If the seat update fails, the resubmission cannot proceed.

Args: same structure as REDEEM_SLOT — all three fields are form field paths.

Example — on a SUBMIT_RFA transition:

{
  "startState": "PENDING_RESUBMISSION_LEVEL_1",
  "event": "SUBMIT_RFA",
  "endStateOne": {
    "stateName": "Pending Officer Processing Level 1",
    "stateNames": null,
    "stateCode": "PENDING_OFFICER_PROCESSING_LEVEL_1",
    "breakingAction": {
      "actionType": "SLOT_REBOOKING",
      "args": {
        "groupIdField": "slotbooking.group.id",
        "timeRangeIdField": "slotbooking.timeRange.id",
        "seatsField": "slotbooking.seats"
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": {
    "authorisedRoles": ["ROLE_CITIZEN", "ROLE_AGENT"],
    "authorisedUsers": null,
    "systemAuthorised": false
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

FETCH_ID_PHOTO

EApplicationContext: APPLICATION, APPLICATION_DETAILS

Fetches the applicant's ID photo from the Identity Engine (NID or child ID), uploads it to MinIO, and saves the filename as a photo application detail entry for use in certificates or officer review.

What it does internally:

  1. Reads the ID type (NID or CHILD_ID) from the fetchValueTypeAttribute field in application details
  2. Reads the ID number from one of the fetchValueAttribute fields
  3. Calls the Identity Engine to fetch the base64-encoded photo
  4. Uploads the decoded photo to MinIO as {idValue}_id_photo.jpg
  5. Adds a photo entry to application details with the stored filename

Placement: nonBreakingActions array — the workflow continues even if the photo fetch fails (failure is logged, does not block the transition). Place alongside the submission NOTIFICATION on the APPLY endState or a post-payment state.

Args:

FieldTypeNotes
fetchValueAttributearrayForm field keys holding the ID number(s), e.g. ["applicantNID", "applicantChildId"]. The action tries each until a match is found
fetchValueTypeAttributestringForm field key whose value is the document type (NID or CHILD_ID)

Example — in nonBreakingActions on the APPLY endState:

{
  "startState": "NEW",
  "event": "APPLY",
  "endStateOne": {
    "stateName": "Payment Pending",
    "stateNames": null,
    "stateCode": "PAYMENT_PENDING",
    "breakingAction": { "actionType": "BILL_ID_GENERATION", "args": null },
    "breakingActions": null,
    "nonBreakingActions": [
      {
        "actionType": "NOTIFICATION",
        "args": {
          "notificationType": "STATIC",
          "staticTemplate": { "...": "..." },
          "dynamicTemplate": null
        }
      },
      {
        "actionType": "FETCH_ID_PHOTO",
        "args": {
          "fetchValueAttribute": ["applicantNID", "applicantChildId"],
          "fetchValueTypeAttribute": "applicantDocumentType.id"
        }
      }
    ],
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": {
    "authorisedRoles": ["ROLE_CITIZEN", "ROLE_AGENT"],
    "authorisedUsers": null,
    "systemAuthorised": false
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

Dependencies:

  • The applicant's NID or child ID must be present in application details
  • The fetched photo is stored and available as the photo detail field for use in certificate templates

SYSTEM_REQUEST_FOR_ACTION

EApplicationContext: APPLICATION, APPLICATION_FEEDBACK

Automatically creates a feedback entry requesting the applicant to correct specific form fields or attachments. System-driven alternative to the officer-triggered REQUEST_FOR_ACTION event. Does not change the workflow state on its own — the state transition is driven by the surrounding event (typically a system TRANSITION). The applicant will be blocked from resubmitting until the flagged fields are corrected.

What it does internally:

  1. Inactivates any existing feedback entries on the application
  2. Creates a new feedback entry with the specified field names, reason, and comment
  3. Filters fieldNames to only include fields that actually exist in the service form definition

Placement: breakingAction (singular) — always breaking. Typically on a system TRANSITION event leading to a PENDING_RESUBMISSION state.

Args:

FieldTypeRequiredNotes
fieldNamesarrayYesList of form field names that need to be edited
feedbackSectionstringNo"FORM" (default), "ATTACHMENT", or "OTHER"
feedbackReasonstringNoReason shown to the user. Default: "System request for field modification"
commentstringNoAdditional comment for the user. Default: "System automatically requested field modifications"
officerSummarystring/nullNoOptional summary for the officer
certificateWithListbooleanNoDefault: false
updateFieldWithDatasetValuebooleanNoDefault: false

Example — system TRANSITION routing to PENDING_RESUBMISSION (form fields):

{
  "startState": "SOME_PROCESSING_STATE",
  "event": "TRANSITION",
  "endStateOne": {
    "stateName": "Pending Resubmission Level 1",
    "stateNames": null,
    "stateCode": "PENDING_RESUBMISSION_LEVEL_1",
    "breakingAction": {
      "actionType": "SYSTEM_REQUEST_FOR_ACTION",
      "args": {
        "fieldNames": ["EBM_DOCUMENT"],
        "feedbackSection": "FORM",
        "feedbackReason": "Request to upload EBM Invoice",
        "comment": "Please upload EBM Invoice",
        "certificateWithList": false,
        "updateFieldWithDatasetValue": false
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": { "authorisedRoles": null, "authorisedUsers": null, "systemAuthorised": true },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

Example — attachment correction:

{
  "actionType": "SYSTEM_REQUEST_FOR_ACTION",
  "args": {
    "fieldNames": ["proofOfPublication"],
    "feedbackSection": "ATTACHMENT",
    "feedbackReason": "System validation failed",
    "comment": "Please upload the proof of publication",
    "certificateWithList": false,
    "updateFieldWithDatasetValue": false
  }
}

TRIGGER_PARENT

EApplicationContext: APPLICATION, APPLICATION_DETAILS

Used in OSC (One Stop Center) child service workflows to fire an event on the parent workflow. When the child reaches a terminal step (payment, approval, rejection), it looks up the parent application by parentApplicationId and calls workflowService.processApplication() on it with the specified event, advancing the parent's state machine.

Placement: always breakingAction or breakingActions — must succeed for the child's transition to complete. Use singular or plural depending on whether other breaking actions accompany it.

Args (TriggerParentArgs):

FieldTypeRequiredNotes
eventstringYesEvent to trigger on the parent — "TRANSITION", "APPROVE", "REJECT", "PAY", etc.
additionalPayloadobject/nullNoOptional extra data to pass with the event to the parent workflow

Common event mappings:

Child eventParent event to trigger
APPROVE / APPROVE_WITH_FORM"APPROVE"
REJECT"REJECT"
PAY"TRANSITION"
EXPIRE_PAYMENT"TRANSITION"

Examples:

Officer approves child → trigger APPROVE on parent (breakingAction singular):

{
  "actionType": "TRIGGER_PARENT",
  "args": {
    "event": "APPROVE"
  }
}

Officer rejects child → trigger REJECT on parent (breakingAction singular):

{
  "actionType": "TRIGGER_PARENT",
  "args": {
    "event": "REJECT"
  }
}

Payment expires → trigger TRANSITION on parent (breakingActions plural, no other breaking actions):

"breakingActions": [
  {
    "actionType": "TRIGGER_PARENT",
    "args": {
      "event": "TRANSITION"
    }
  }
]

Full transition example — EXPIRE_PAYMENT on child:

{
  "startState": "PAYMENT_PENDING",
  "event": "EXPIRE_PAYMENT",
  "endStateOne": {
    "stateName": "Payment Expired",
    "stateNames": null,
    "stateCode": "PAYMENT_EXPIRED",
    "breakingAction": null,
    "breakingActions": [
      {
        "actionType": "TRIGGER_PARENT",
        "args": { "event": "TRANSITION" }
      }
    ],
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": null,
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

Only use in child service workflows within an OSC setup. Do not use in standalone service workflows. Throws NotFoundException if the child application has no parentApplicationId.


APPLY_OR_RESUBMIT_CHILD_APPLICATION

EApplicationContext: APPLICATION, APPLICATION_SUMMARY, APPLICATION_DETAILS, APPLICATION_STATE_TRACKER, PAYMENT_TRANSACTION, APPLICATION_FEEDBACK, APPLICATION_CERTIFICATES

Used in OSC parent service workflows to spawn or resubmit a child application for a given sub-service. When the parent workflow reaches this action, it:

  1. Looks up the child service by serviceCode
  2. Searches for an existing child application linked to this parent (parentApplicationId)
  3. If none found → creates a new child application
  4. If one found → evaluates childResubmitConditions against the child's context; if all conditions pass, resubmits it; otherwise skips

Placement: breakingAction (singular) — must succeed for the parent to continue.

Args:

FieldTypeRequiredNotes
serviceCodestringYesThe service code of the child service to apply for (e.g. "SRVD13F0126")
childResubmitConditionsarray/nullNoList of conditions checked against the existing child application. ALL must pass (AND logic) for a resubmit to trigger. If null, always creates new
updateFieldWithDatasetValuebooleanNoDefault: false
excludeCurrentApplicationbooleanNoDefault: true
certificateWithListbooleanNoDefault: false

childResubmitConditions — each condition:

FieldNotes
parameterKey in the child application's context — can be a form field key (from application details) or a payload key like "applicationState"
valueExpected value to compare against
comparatorComparison operator — "EQ", "NEQ", etc.

Example — spawn child, skip if condition not met:

{
  "startState": "PUSHED_INITIATE_AMENDMENT_PUSH",
  "event": "INITIATE_PUSH",
  "endStateOne": {
    "stateName": "Pushed Amend company details",
    "stateNames": null,
    "stateCode": "PUSHED_AMEND_COMPANY_DETAILS",
    "breakingAction": {
      "actionType": "APPLY_OR_RESUBMIT_CHILD_APPLICATION",
      "args": {
        "serviceCode": "SRVD13F0126",
        "childResubmitConditions": [
          {
            "parameter": "applicationState",
            "value": "PENDING_RESUBMISSION",
            "comparator": "EQ"
          }
        ],
        "updateFieldWithDatasetValue": false,
        "excludeCurrentApplication": true,
        "certificateWithList": false
      }
    },
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "TRANSITION",
    "position": null
  },
  "endStateTwo": {
    "stateName": "Pushed Amend company details",
    "stateNames": null,
    "stateCode": "PUSHED_AMEND_COMPANY_DETAILS",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "TRANSITION",
    "position": null
  },
  "endStateCondition": {
    "endStateTwo": [
      {
        "parameter": "DO_YOU_WANT_TO_MODIFY_COMPANY_DETAILS",
        "value": "No",
        "comparator": "EQ"
      }
    ],
    "validationStrategy": null,
    "validationStrategyIdentifierField": null,
    "childReadyStates": null,
    "duplicateCheckApplicationStates": null,
    "validationConfig": null
  },
  "authorisation": {
    "authorisedRoles": null,
    "authorisedUsers": null,
    "systemAuthorised": true
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

endStateCondition.endStateTwo routes to endStateTwo (no action, skip) when the applicant answered "No" to modifying that section. endStateOne (spawn/resubmit child) is taken when the condition is NOT met.


endStateCondition — Conditional Routing

Controls which end state a transition takes when there are two possible outcomes.

Form field routing (endStateTwo conditions)

Routes to endStateTwo when ALL listed conditions evaluate to true against the application context. If any condition fails, endStateOne is taken.

Context evaluated against: application details (form field values stored in DB) merged with the application payload (includes applicationState and other system keys). Payload values override application details.

Condition fields:

FieldNotes
parameterKey to look up in the merged context — a form field key (e.g. "DO_YOU_WANT_TO_MODIFY_COMPANY_DETAILS") or a system key like "applicationState"
valueExpected value
comparator"EQ" (equal), "NEQ" (not equal), and others supported by applicationValidationService.compareValues()
"endStateCondition": {
  "endStateTwo": [
    {
      "parameter": "DO_YOU_WANT_TO_MODIFY_COMPANY_DETAILS",
      "value": "No",
      "comparator": "EQ"
    }
  ],
  "validationStrategy": null,
  "validationStrategyIdentifierField": null,
  "childReadyStates": null,
  "duplicateCheckApplicationStates": null,
  "validationConfig": null
}

Child readiness check (childReadyStates)

Used exclusively with the NOTIFY_MASTER_CHILD_READY event. Routes to endStateOne (advance) when ALL child applications are in one of the listed states; stays in endStateTwo (wait) if any child is not yet ready.

Uses countChildrenNotInStates(parentId, childReadyStates) — when count = 0, all children are ready.

"endStateCondition": {
  "endStateTwo": null,
  "validationStrategy": null,
  "validationStrategyIdentifierField": null,
  "childReadyStates": [
    "PENDING_OFFICER_PROCESSING",
    "REJECTED",
    "APPROVED"
  ],
  "duplicateCheckApplicationStates": null,
  "validationConfig": null
}

Full NOTIFY_MASTER_CHILD_READY transition:

{
  "startState": "WAITING_FOR_ALL_CHILD_APPLICATIONS",
  "event": "NOTIFY_MASTER_CHILD_READY",
  "endStateOne": {
    "stateName": "All Children Ready - Move Forward",
    "stateNames": null,
    "stateCode": "PUSHED_TO_PARTNER_LEVEL_1",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": "INITIATE_PUSH",
    "position": null
  },
  "endStateTwo": {
    "stateName": "Child Not Ready - Wait",
    "stateNames": null,
    "stateCode": "WAITING_FOR_ALL_CHILD_APPLICATIONS",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateCondition": {
    "endStateTwo": null,
    "validationStrategy": null,
    "validationStrategyIdentifierField": null,
    "childReadyStates": ["PENDING_OFFICER_PROCESSING", "REJECTED", "APPROVED"],
    "duplicateCheckApplicationStates": null,
    "validationConfig": null
  },
  "authorisation": {
    "authorisedRoles": null,
    "authorisedUsers": null,
    "systemAuthorised": true
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

endStateTwo loops back to WAITING_FOR_ALL_CHILD_APPLICATIONS (same state) with nextEvent: null — the parent simply stays put until more children complete and fire NOTIFY_MASTER_CHILD_READY again.


OSC Parent-Child Pattern (Summary)

How the full parent ↔ child coordination works:

Child workflow responsibilities:

  • Terminal transitions (APPROVE, REJECT, PAY, EXPIRE_PAYMENT) use TRIGGER_PARENT with the appropriate event to signal the parent
  • NOTIFY_MASTER_CHILD_READY is the parent event that aggregates all child completions

Parent workflow responsibilities:

  • Uses APPLY_OR_RESUBMIT_CHILD_APPLICATION to conditionally spawn each sub-service child
  • Parks at WAITING_FOR_ALL_CHILD_APPLICATIONS with nextEvent: "NOTIFY_MASTER_CHILD_READY"
  • Uses endStateCondition.childReadyStates to define which child states count as "done"
  • Uses TRIGGER_CHILD_SERVICE_EVENT to cascade events (e.g. PAY, PROCESS_PAYMENT) to all successful children

TRIGGER_CHILD_SERVICE_EVENT

EApplicationContext: APPLICATION

Used in OSC parent service workflows to fire an event on all successful child applications at once. Looks up children by parentApplicationId that have EValidationStatus.SUCCESS, then sends the specified event to each via Pulsar.

Placement: breakingAction (singular).

Args (TriggerChildEventArgs):

FieldTypeRequiredNotes
childrenApplicationEventstringYesEvent to trigger on each successful child — e.g. "PAY", "PROCESS_PAYMENT"
{
  "actionType": "TRIGGER_CHILD_SERVICE_EVENT",
  "args": {
    "childrenApplicationEvent": "PAY"
  }
}

Only fires on children with SUCCESS validation status. Children that failed validation are skipped.


CANCEL

EApplicationContext: APPLICATION, APPLICATION_STATE_TRACKER

Cancels the application. Triggered by an officer or authorised agent via REST API (POST /cancel). Requires CANCEL_APPLICATION permission. Cannot be applied to applications already in a FINAL state.

Who triggers it: Officer / agent (HTTP API call) — NOT system-triggered.

Transition structure — typically on every non-terminal state:

{
  "startState": "PENDING_OFFICER_PROCESSING",
  "event": "CANCEL",
  "endStateOne": {
    "stateName": "Cancelled",
    "stateNames": null,
    "stateCode": "CANCELLED",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": null,
  "endStateCondition": null,
  "authorisation": null,
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

PROCESS_BULK_APPLICATION

System event that initiates processing of a bulk master application. Loads the application context and payload, then returns the workflow result for state transition. Used at the entry point of OSC/bulk parent workflows.

Who triggers it: System (nextEvent auto-advance or background job).

No special args. No actions attached — just a state advance that sets up context for subsequent APPLY_OR_RESUBMIT_CHILD_APPLICATION chains.


VALIDATE_AND_RECORD_STATUS

System event for child applications in bulk workflows. Runs a configured validation strategy against the child application, records the result (SUCCESS/FAILED) in application_validation_state, then routes to endStateOne (passed) or endStateTwo (failed).

Who triggers it: System (nextEvent auto-advance).

Requires endStateCondition.validationStrategy — specifies which validator to run (e.g. "CONFIGURABLE", "TRAFFIC_FINE").

Routes:

  • endStateOne → validation passed
  • endStateTwo → validation failed
{
  "startState": "SUBMITTED",
  "event": "VALIDATE_AND_RECORD_STATUS",
  "endStateOne": {
    "stateName": "Validation Passed",
    "stateNames": null,
    "stateCode": "VALIDATION_PASSED",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateTwo": {
    "stateName": "Validation Failed",
    "stateNames": null,
    "stateCode": "VALIDATION_FAILED",
    "breakingAction": null,
    "breakingActions": null,
    "nonBreakingActions": null,
    "nextEvent": null,
    "position": null
  },
  "endStateCondition": {
    "endStateTwo": null,
    "validationStrategy": "CONFIGURABLE",
    "validationStrategyIdentifierField": null,
    "childReadyStates": null,
    "duplicateCheckApplicationStates": null,
    "validationConfig": null
  },
  "authorisation": {
    "authorisedRoles": null,
    "authorisedUsers": null,
    "systemAuthorised": true
  },
  "position": null,
  "eventNames": null,
  "eventConfigs": null
}

PROCESS_PAYMENT

System event triggered on child applications by the parent's TRIGGER_CHILD_SERVICE_EVENT action. Verifies the child's payment with the payment gateway and updates the payment transaction record (status → PAID, transaction ID, payment method, timestamp).

Who triggers it: Parent workflow via TRIGGER_CHILD_SERVICE_EVENT with childrenApplicationEvent: "PROCESS_PAYMENT".

Does not throw on failure — payment verification errors are logged but do not block the child's workflow transition.

Typical usage: After the parent PAY event, fire PROCESS_PAYMENT on all successful children to sync their payment transaction records with the gateway.

On this page