Getting Started with Process Orchestration
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.
| Role | What they do |
|---|---|
| Maker | Creates and submits destinations; stages and submits BPMN process definitions; starts process instances |
| Checker | Reviews 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
| Step | Who | Action |
|---|---|---|
| 1 | Maker | Creates the artefact (destination or process definition) |
| 2 | Maker | Submits it for approval |
| 3 | Checker | Reviews the content |
| 4 | Checker | Approves (or rejects with a reason) |
| 5 | Checker | Makes the artefact current (required before execution) |
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_CURRENCYenabled 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&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.
| Header | Value |
|---|---|
processInstanceId | The instance ID for monitoring |
processInstanceStatus | COMPLETED 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:
| Variable | Description |
|---|---|
{taskId}_status | HTTP status code returned by the external system |
{taskId}_response | Parsed JSON response body |
{taskId}_responseRaw | Raw 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
| Symptom | Likely cause | Resolution |
|---|---|---|
HTTP 404 on :start | Process definition not found or not in CURRENT status | Confirm the BPMN was staged, approved, and made current |
HTTP 422 on :approve | Self-approval attempted — Maker and Checker are the same user | Use a separate user account for the Checker role |
Instance FAILED at a call-destination step | External API returned a non-2xx response, or the destination is not CURRENT | Check {taskId}_status and {taskId}_responseRaw in variable history; verify destination status |
DATE_ON_LAST_RATE_DATE from Mambu currencies API | A rate for that date already exists | Use a different startDate, or delete the existing rate first |
What's next
- BPMN Authoring — all supported BPMN elements, service task types, gateways, human tasks, async execution, and error handling
- Worked Examples — four implementation patterns with full BPMN and HTTP examples
- Destinations API — full endpoint reference for destination management
- Staged Process Definitions API — full endpoint reference for process definition deployment
- Process Instances API — full endpoint reference for starting and monitoring instances