About Orchestrating Systems
How the two-phase commit protocol coordinates bridges to guarantee atomic transactions
When an intent enters the orchestration stage, the ledger must coordinate multiple external systems to move value atomically. The mechanism for this coordination is the two-phase commit (2PC) protocol — a conversation between the ledger and every bridge involved in the intent that guarantees either all participants commit or all abort.
This page covers the protocol itself: the phases, the proofs, the error boundaries, and the differences between the debit and credit sides.
The protocol
The 2PC protocol has four phases. The sequence diagram below shows the happy path — both bridges prepare and commit successfully.
Prepare phase
The prepare phase is where bridges first get involved. The hub sends prepare requests to every bridge that participates in the intent — POST /v2/debits to the source bridge (where value leaves) and POST /v2/credits to the target bridge (where value arrives). These calls happen simultaneously.
Each bridge follows the same protocol:
Acknowledge immediately. The bridge responds with 202 Accepted to confirm it received the request. This is not approval — it is a receipt. The actual decision comes later via a proof.
Validate the operation. The bridge checks its backend: does the account exist? Is it active? For debits, is there sufficient balance? Are there any holds, freezes, or compliance blocks that prevent the operation?
Reserve funds (debit side). If the bridge is handling a debit and validation passes, it typically places a hold or creates a pending transaction in the banking core. The funds are earmarked but not yet finally deducted.
Validate the target (credit side). If the bridge is handling a credit, it confirms the target account can receive funds — it exists, is open, accepts the currency, and has no restrictions.
Submit a proof. Once processing completes, the bridge posts a cryptographic proof to POST /v2/intents/:handle/proofs. If everything is good, the proof carries status: prepared. If something is wrong, the proof carries status: failed along with a reason code and detail message.
The hub waits for proofs from all participants. Only when every bridge has submitted a prepared proof does the protocol advance to commit. If any bridge submits a failed proof, the hub moves to abort instead.
The prepare phase is where most real-world failures surface — insufficient funds, closed accounts, compliance holds, network timeouts. This is by design. The entire point of the two-phase protocol is to discover problems before any irreversible action is taken.
Commit phase
The commit phase runs only when all participants have prepared successfully. The hub sends POST /v2/debits/:handle/commit to the source bridge and POST /v2/credits/:handle/commit to the target bridge, again simultaneously.
Each bridge:
Acknowledges immediately with 202 Accepted.
Finalizes the operation. On the debit side, this means converting the hold into a confirmed deduction. On the credit side, this means depositing the funds into the target account.
Submits a proof with status: committed. The proof may include a coreId — the transaction identifier from the banking core — which is essential for reconciliation.
Commit must succeed. Once the hub sends a commit, the bridge has made a promise that it will carry through. If the banking core is temporarily unavailable, the bridge must retry internally. Returning a failure from commit is not an option — it would leave the distributed transaction in an inconsistent state. See Handle Errors and Retries for recovery patterns.
Abort phase
The abort phase runs when any participant fails during prepare. The hub sends POST /v2/debits/:handle/abort and POST /v2/credits/:handle/abort to the respective bridges.
Each bridge reverses whatever side effects it created during prepare:
- If a debit hold was placed, release it.
- If a pending transaction was created, reverse it.
- If no side effects were created (because the bridge was the one that failed), acknowledge the abort and move on.
The bridge submits a proof with status: aborted. If the reversal created a transaction in the banking core, the proof should include the coreId of that reversal for audit purposes.
Abort must succeed, just like commit. The bridge cannot refuse to clean up. If the reversal fails due to a transient error, the bridge must retry until it succeeds. An incomplete abort leaves phantom holds on customer accounts.
Final status notification
After all participants have committed or aborted, the intent reaches its final status: completed or rejected. At this point, the processing pipeline moves to the effects stage — any registered effects that match the intent's status transition fire asynchronously. Effects can deliver notifications as webhooks to any URL, or through a bridge's POST /v2/effects/:handle endpoint if the bridge declares the events trait.
This separation keeps the orchestration interface focused on the 2PC protocol. Status notifications are a downstream concern handled by the effects system, not by the bridge's core credit and debit interface.
The proof system
Proofs are the language bridges use to communicate with the hub. Every phase transition — prepared, committed, failed, aborted — is reported through a proof submitted to POST /v2/intents/:handle/proofs.
A proof is a cryptographically signed statement using Ed25519-v2. Each proof contains: method (the signing algorithm), public (the signer's public key), digest (a hash of the signed content), and result (the signature). This allows the hub to verify that the proof genuinely came from the bridge's registered signer.
The custom object carries the phase-specific payload:
- status —
prepared,committed,failed, oraborted - handle — the debit or credit entry handle (e.g.,
deb_01u9RGCevt4rEkRMV) - coreId — the transaction ID from the banking core, for reconciliation
- moment — ISO 8601 timestamp of when the operation occurred
- reason and detail — error information (only for
failedstatus)
Example — successful prepare:
{
"method": "ed25519-v2",
"public": "XFQijCr5V490hvUzaIWkc7WRIBh1/JlwiW0mMbmqNHM=",
"digest": "a1b2c3d4e5f6...",
"result": "f6e5d4c3b2a1...",
"custom": {
"status": "prepared",
"handle": "deb_01u9RGCevt4rEkRMV",
"coreId": "TXN-2026-0323-00847",
"moment": "2026-03-23T14:30:00.000Z"
}
}Example — failed prepare:
{
"method": "ed25519-v2",
"public": "XFQijCr5V490hvUzaIWkc7WRIBh1/JlwiW0mMbmqNHM=",
"digest": "d4e5f6a1b2c3...",
"result": "b2a1f6e5d4c3...",
"custom": {
"status": "failed",
"handle": "deb_01u9RGCevt4rEkRMV",
"reason": "insufficient-funds",
"detail": "Account 1234567890 has available balance of 50.00, requested 100.00",
"moment": "2026-03-23T14:30:00.000Z"
}
}Error boundaries
The 2PC protocol creates a sharp dividing line between two error regimes:
Before commit/abort (during prepare): errors are expected. If a bridge cannot prepare, it submits a failed proof. The hub aborts the intent and all participants clean up. This is the normal failure path.
After commit/abort is received: errors are not acceptable. The hub has decided the outcome. The bridge's job is to execute it. If the banking core is down, retry. If a network glitch interrupts, re-execute. The bridge must have internal mechanisms — retry queues, idempotency keys, dead-letter handling — to guarantee eventual completion.
This asymmetry is the core insight of 2PC. The prepare phase absorbs all the uncertainty. By the time commit or abort arrives, every participant has confirmed it can proceed. The remaining work is execution, not decision-making.
Debit vs. credit differences
Both sides follow the same protocol structure, but the banking operations differ.
Debit (source side) is about taking money away. During prepare, the bridge validates the source account and typically places a hold to reserve funds. During commit, the bridge finalizes the deduction. During abort, the bridge releases the hold.
Credit (target side) is about delivering money. During prepare, the bridge validates the target account. During commit, the bridge deposits the funds. During abort, the bridge reverses any pending entries.
The key difference is in the risk profile. A debit bridge must be confident during prepare that funds will still be available at commit time — holds are standard practice. A credit bridge has simpler prepare logic but carries the responsibility of actually crediting the account during commit.
Both sides share the same rule: commit and abort must succeed.
Performance and scaling
The 2PC protocol is designed for high-throughput real-time payment networks. In production deployments, the Minka Ledger has processed over 10,000 transactions per second with sub-millisecond ledger latency, achieving 99.998% uptime across network participants.
The async proof model is the key to this throughput. Because bridges acknowledge immediately and report results separately, the ledger does not block on external system latency. A banking core that takes 200ms to validate a debit does not hold up the entire pipeline — the ledger tracks in-flight proofs asynchronously and advances the protocol as they arrive.
For your own deployment, throughput is bounded by:
- Bridge response time — how fast your banking core validates operations. Design for sub-100ms prepare responses under normal load.
- Proof processing rate — how many proofs per second the ledger can receive and process. Contact Minka for capacity planning on large deployments.
- Concurrency — the bridge SDK processes entries in parallel worker pools. Configure
PROCESSOR_WORKERSin your.envto match your banking core's concurrency limits.
For high-availability bridge deployments running multiple instances, each instance independently processes entries. The bridge SDK uses database-backed idempotency keys to prevent duplicate operations if two instances receive the same entry. See Handle Errors and Retries for the idempotency contract.
About Processing Intents
Intents, claims, payment initiation, and the three-stage processing pipeline.
Build the Credit and Debit Interface
Full request and response payloads for the debit and credit endpoints.
Handle Errors and Retries
Retry mechanics, idempotency, and the commit-never-fails contract in practice.