Skip to main content

Getting Started with Process Orchestration

Non-Production Preview

Process Orchestration is currently available as a Non-Production Preview (NPP) feature, available to a limited number of customers. You should not process production data with NPP features. To request access, contact your Mambu Customer Success Manager. For more information, see Mambu Release Cycle — Feature Release Status.


This guide walks you through the prerequisites and a complete working example — from registering a destination to starting a process instance and inspecting its result.

For the full API reference, see the Process Orchestration API overview.

How it works

External Orchestration runs a BPMN engine inside Mambu. Each tenant has its own isolated engine instance, backed by that tenant's database. The runtime has three resource families:

  • Destinations — named, authenticated connections to external systems. Credentials are stored server-side, encrypted at rest. BPMN processes reference destinations by their logical externalId; the same BPMN runs against different environments by updating the destination URL.
  • Process Definitions — BPMN 2.0 files uploaded by a maker and approved by a checker before they can execute. Multiple versions can exist; the latest approved version is always used.
  • Process Instances — a running or completed execution of a definition. Execution state and history are persisted and queryable.

The process start call returns immediately with a processInstanceId. If the process contains asynchronous steps, it continues on the engine's worker pool after the HTTP response is sent. Callers can poll the monitoring endpoints to track progress.

Prerequisites

Feature enablement

Process Orchestration must be enabled on your tenant before any Process Orchestration API is accessible. Contact your Mambu Customer Success Manager to enable it on your sandbox environment.

Two user accounts

Process Orchestration enforces maker-checker controls at every stage. You need two distinct Mambu user accounts. The platform rejects self-approval with HTTP 422.

RoleWhat they do
MakerCreates and submits destinations; stages and submits BPMN process definitions; starts process instances
CheckerReviews and approves destinations; reviews and approves process definitions; makes artefacts current. Must be a different user than the Maker

Content-Type header

All Process Orchestration API requests require:

Accept: application/vnd.mambu.v2+json

When sending a request body, also include:

Content-Type: application/vnd.mambu.v2+json

Exception: some Mambu CBE APIs (such as the currencies API) accept only Content-Type: application/json. When a process calls back into Mambu via a destination, set the correct content-type in the BPMN using <mbu:header name="Content-Type" value="application/json"/>.

Credentials

Generate a base64-encoded string for HTTP Basic auth:

echo -n 'username:password' | base64

The maker-checker governance model

Both destinations and process definitions must pass through a maker-checker approval before they can be used in executions. This ensures no single user can deploy and run a process without a second review.

DRAFT ──[maker: submit]──► PENDING_APPROVAL ──[checker: approve]──► APPROVED ──[checker: make-current]──► CURRENT
└──[checker: reject]──► REJECTED
StepWhoAction
1MakerCreates the artefact (destination or process definition)
2MakerSubmits it for approval
3CheckerReviews the content
4CheckerApproves (or rejects with a reason)
5CheckerMakes the artefact current (required before execution)
note

The :make-current step is required after :approve. An artefact in APPROVED status is not yet active and cannot be used by running processes.

If the checker rejects a submission, the artefact moves to REJECTED status with a reason. The maker can then upload a corrected version and restart the cycle from step 1.

Quickstart: your first External Orchestration process

This tutorial builds a process that fetches the current GBP/EUR exchange rate from frankfurter.dev — a free public API requiring no API key — and writes it to Mambu's currency exchange-rate API.

What you will build:

start
└─► fetchFxRate (GET frankfurter.dev/v1/latest)
└─► extractRate (extract rate + effective date)
└─► updateMambuRate (POST /api/currencies/EUR/rates)
├─► [success] buildResult ──► end
└─► [HTTP error] buildErrorResult ──► end

Prerequisites for this tutorial:

  • Process Orchestration and MULTI_CURRENCY enabled on your tenant
  • EUR as an active currency, with GBP as the base currency
  • Two Mambu user accounts (Maker and Checker)

Step 1 — Register destinations

A destination is a named connection to an external system. Register one for the FX rate API and one pointing back at Mambu itself.

Register the FX rate API:

curl -X POST "https://YOUR_HOST/api/destinations" \
-H "Content-Type: application/vnd.mambu.v2+json" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD" \
-d '{
"externalId": "frankfurter-api",
"name": "Frankfurter ECB FX Rates",
"baseUrl": "https://api.frankfurter.dev",
"authType": "BASIC",
"secureProperties": {},
"verifyTlsCertificate": true
}'

Register Mambu itself (so the BPMN can POST the rate back to your tenant):

curl -X POST "https://YOUR_HOST/api/destinations" \
-H "Content-Type: application/vnd.mambu.v2+json" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD" \
-d '{
"externalId": "mambu",
"name": "Mambu CBE",
"baseUrl": "https://YOUR_HOST",
"authType": "BASIC",
"secureProperties": {
"username": "MAKER_USER",
"password": "MAKER_PASSWORD"
},
"verifyTlsCertificate": true
}'

Test connectivity before proceeding. A "status": 200 in the response body confirms the destination is reachable:

curl -X POST "https://YOUR_HOST/api/destinations/frankfurter-api/test" \
-H "Content-Type: application/vnd.mambu.v2+json" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD" \
-d '{"method": "HEAD", "path": "/v1/latest", "headers": {}}'

After creating and testing, follow the Destinations API maker-checker lifecycle to submit, approve, and make each destination current before proceeding. Both frankfurter-api and mambu must be in CURRENT status.


Step 2 — Write the BPMN

Every Process Orchestration BPMN must declare the Mambu extension namespace (xmlns:mbu) and set targetNamespace="process-orchestration". Save the following as fx-rate-sync.bpmn:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:mbu="http://mambu.com/bpmn"
targetNamespace="process-orchestration">

<process id="fxRateSync" name="FX Rate Sync" isExecutable="true">

<startEvent id="start"/>

<!-- Fetch GBP/EUR rate. Response stored in fetchFxRate_response, status in fetchFxRate_status -->
<serviceTask id="fetchFxRate" name="Fetch FX Rate" mbu:type="call-destination">
<extensionElements>
<mbu:destination>frankfurter-api</mbu:destination>
<mbu:method>GET</mbu:method>
<mbu:path>/v1/latest?from=GBP&amp;to=EUR</mbu:path>
</extensionElements>
</serviceTask>

<!-- Extract rate value and effective date from process input -->
<serviceTask id="extractRate" name="Extract Rate" mbu:type="assign-variable">
<extensionElements>
<mbu:variable name="gbpToEurRate" value="${fetchFxRate_response.rates.EUR}"/>
<mbu:variable name="startDate" value="${processInput.startDate}"/>
</extensionElements>
</serviceTask>

<!-- Write the rate to Mambu. Currencies API requires Content-Type: application/json -->
<serviceTask id="updateMambuRate" name="Update EUR Rate" mbu:type="call-destination">
<extensionElements>
<mbu:destination>mambu</mbu:destination>
<mbu:method>POST</mbu:method>
<mbu:path>/api/currencies/EUR/rates</mbu:path>
<mbu:header name="Content-Type" value="application/json"/>
<mbu:header name="Accept" value="application/vnd.mambu.v2+json"/>
<mbu:payload>{"buyRate": ${gbpToEurRate}, "sellRate": ${gbpToEurRate}, "startDate": "${startDate}"}</mbu:payload>
</extensionElements>
</serviceTask>

<!-- Catch any HTTP error from the Mambu POST and route to an error result -->
<boundaryEvent id="updateMambuRateError" attachedToRef="updateMambuRate">
<errorEventDefinition/>
</boundaryEvent>

<!-- Success: return rate and HTTP status as the process result -->
<serviceTask id="buildResult" name="Build Result" mbu:type="build-json">
<extensionElements>
<mbu:jsonTemplate>{"gbpToEurRate": "${r}", "mambuStatus": "${s}"}</mbu:jsonTemplate>
<mbu:jsonValues>{"r": "${gbpToEurRate}", "s": "${updateMambuRate_status}"}</mbu:jsonValues>
<mbu:resultVariable>processResult</mbu:resultVariable>
</extensionElements>
</serviceTask>

<!-- Error: surface what the upstream system returned -->
<serviceTask id="buildErrorResult" name="Build Error Result" mbu:type="build-json">
<extensionElements>
<mbu:jsonTemplate>{"outcome": "FAILED", "status": "${s}", "response": "${r}"}</mbu:jsonTemplate>
<mbu:jsonValues>{"s": "${updateMambuRate_status}", "r": "${updateMambuRate_response}"}</mbu:jsonValues>
<mbu:resultVariable>processResult</mbu:resultVariable>
</extensionElements>
</serviceTask>

<endEvent id="end"/>
<endEvent id="endError"/>

<sequenceFlow id="f1" sourceRef="start" targetRef="fetchFxRate"/>
<sequenceFlow id="f2" sourceRef="fetchFxRate" targetRef="extractRate"/>
<sequenceFlow id="f3" sourceRef="extractRate" targetRef="updateMambuRate"/>
<sequenceFlow id="f4" sourceRef="updateMambuRate" targetRef="buildResult"/>
<sequenceFlow id="f5" sourceRef="buildResult" targetRef="end"/>
<sequenceFlow id="f6" sourceRef="updateMambuRateError" targetRef="buildErrorResult"/>
<sequenceFlow id="f7" sourceRef="buildErrorResult" targetRef="endError"/>
</process>
</definitions>

Step 3 — Stage and approve the process definition

Maker — upload the BPMN:

curl -X POST "https://YOUR_HOST/api/stagedprocessdefinitions" \
-H "Content-Type: application/octet-stream" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD" \
--data-binary @fx-rate-sync.bpmn

The response contains the staged definition id. Note it — the checker needs it.

Checker — approve:

curl -X POST "https://YOUR_HOST/api/stagedprocessdefinitions/{id}:approve" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "CHECKER_USER:CHECKER_PASSWORD"

Checker — make current (deploys the definition to the engine, making it executable):

curl -X POST "https://YOUR_HOST/api/stagedprocessdefinitions/{id}:make-current" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "CHECKER_USER:CHECKER_PASSWORD"

To reject with a reason:

curl -X POST "https://YOUR_HOST/api/stagedprocessdefinitions/{id}:reject" \
-H "Content-Type: application/vnd.mambu.v2+json" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "CHECKER_USER:CHECKER_PASSWORD" \
-d '{"reason": "Destination code does not match registered name"}'

Step 4 — Start the process

The request body is passed to the BPMN as processInput. Any field in the body is accessible inside the process as ${processInput.fieldName}.

curl -X POST "https://YOUR_HOST/api/processinstances/fxRateSync:start" \
-H "Content-Type: application/vnd.mambu.v2+json" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD" \
-d '{"startDate": "2026-07-14T00:00:00+00:00"}'

On success (200 OK) — the response body is the processResult the BPMN set:

{"gbpToEurRate": 1.1584, "mambuStatus": 201}

Response headers always include processInstanceId and processInstanceStatus.

HeaderValue
processInstanceIdThe instance ID for monitoring
processInstanceStatusCOMPLETED or RUNNING

Step 5 — Monitor and debug

Check instance status:

curl -X GET "https://YOUR_HOST/api/processinstances/{processInstanceId}" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD"

A COMPLETED instance has a non-null endTime. A RUNNING instance has null endTime and is either waiting at a human task or executing async steps.

Inspect process variables (primary debug tool):

curl -X GET "https://YOUR_HOST/api/processinstances/{processInstanceId}/history/variables" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD"

For each call-destination task, three variables are produced automatically:

VariableDescription
{taskId}_statusHTTP status code returned by the external system
{taskId}_responseParsed JSON response body
{taskId}_responseRawRaw response body string

If a step failed unexpectedly, these variables show exactly what the external system returned.

View step-by-step activity trace:

curl -X GET "https://YOUR_HOST/api/processinstances/{processInstanceId}/history/activities" \
-H "Accept: application/vnd.mambu.v2+json" \
-u "MAKER_USER:MAKER_PASSWORD"

Common errors

SymptomLikely causeResolution
HTTP 404 on :startProcess definition not found or not in CURRENT statusConfirm the BPMN was staged, approved, and made current
HTTP 422 on :approveSelf-approval attempted — Maker and Checker are the same userUse a separate user account for the Checker role
Instance FAILED at a call-destination stepExternal API returned a non-2xx response, or the destination is not CURRENTCheck {taskId}_status and {taskId}_responseRaw in variable history; verify destination status
DATE_ON_LAST_RATE_DATE from Mambu currencies APIA rate for that date already existsUse a different startDate, or delete the existing rate first

What's next