Follow payment statuses with webhooks
An endpoint on the link's System and chain subscribes to bank-rail.credit.* and bank-rail.cash-out.* to follow committed milestones. The default state subscriptions skip these names. Credits report admission, minting's completed outcome, a hold and its resolution, or a full cancellation or recall. Approvals and submissions send no intermediate event. Cash-outs report requested, instructed, paid, returned or released. See the event catalogue for the exact status rules and the accounting feed for decimal amounts and posting decisions.
Overview
The bank rail keeps a reserve token in step with money at a bank. It credits a client's wallet when a bank transfer reaches the organisation's reserve account, the bank account that backs the token, and pays a client's redemption back to their bank account. The platform talks to the bank over FIX, the Financial Information eXchange messaging protocol. Its FIX counterparty is the bank FIX simulator that runs inside the platform or, in external mode, your bank's FIX acceptor, as Set up a connection describes. The bank rail routes sit under /, except the rail wallet routes under / and the routes a participant calls for their own account under /api/v2/users/me. The Console guide shows the same flows on screen.
Every /api/v2/bank-rail route requires the Bank rail manager role on the wallet the request acts through. Paths in the tables are relative to /api/v2/bank-rail, and paths that start with /users/me are relative to /api/v2. The participant's own writes, POST /users/ and POST /users/, need the participant's signed-in session, and an API key is refused.
A client follows their own money in the Investor portal instead of calling these routes: the Deposit dialog's By bank transfer method shows where to pay, Transfers lists each bank payment and cash-out, and Account > Bank accounts, in the Investor portal or the Console account menu, holds the payout accounts. The two participant reads behind those screens are GET /users/ and GET /users/.
The Console shows the same records on the tabs of a bank rail token. Each action calls the route in the tables below, and a refusal appears as its own message.
- Payments lists each recall's state and opens a recall sheet with the actions the recall's state allows.
- Cash-outs marks a returned payout and links to its re-credit.
- Settings shows the credit threshold and the mint cap, and a pending limit request with “Approve” and “Reject”.
A bank account always needs a manager other than its proposer to approve it, and so does the execution or abandoning of a recall. The other decisions on this page wait for a second manager only while the bank-rail family of the approval policy, the organisation's per-area maker-checker switch, is on. The family is off by default and is set at PUT /api/ or under Approvals in the organisation's General settings. It covers:
- a cash-out over the checker threshold
- a credit over the credit threshold
- a parked-credit resolution
- a reserve adjustment
- a raised credit threshold or mint cap
- the completion or abandoning of a rail wallet rotation
With the family off, each of these goes ahead without a second manager, and the platform records that the approval policy waived it. A rotation's abandon or completion then runs on the next read of the rotation, and a raised mint limit still needs your wallet verification. With the family on, the manager who proposed a request cannot decide it, and a decision from an impersonated session is refused with impersonated-session. When the family's approval count is greater than 1, that many different people must approve each request, and each of them must be an organisation owner.
The setup writes (connections, the logon password, token and client links, client accounts, escrows and rail wallet rotations) are administration changes in the Custody administration group. While the approval policy protects that group, each one is held until another owner approves it, as General settings describes.
POST /connections/, POST /users/, POST /rail- and POST /connections/ honour an Idempotency-Key, and a cash-out on a delegate link, one of the two supply modes that Link the reserve token describes, requires one. The other writes are not replayed by key. Idempotency and async settlement explains the contract.
The routes form the bankRail operation group of the Platform API. The SDK exposes them in the generated route tree, as Calling routes describes. The CLI derives a command from each route path by the verb-first rule: GET /api/ is dalp get bank-. Run dalp get bank- to list the commands your build has, as CLI commands explains. Refusals use the bank rail codes DALP-14113 to DALP-14123, DALP-14126 to DALP-14138, DALP-14391 for credit resolutions, DALP-14422 to DALP-14424 for cash-outs, and DALP-14139 and DALP-14140 for daily reconciliation reads and runs. The escrow routes refuse with DALP-9701 to DALP-9703. All are listed in the error index.
Activating a connection shows the three surfaces side by side:
curl -X POST "https://your-instance.example.com/api/v2/bank-rail/connections/$CONNECTION_ID/transitions" \
-H "X-Api-Key: sm_dalp_..." -H "Content-Type: application/json" \
-d '{ "transition": "activate" }'
dalp create bank-rail connections transitions "$CONNECTION_ID" --transition activateawait dalp.api.bankRail.connectionTransition({
connectionId,
transition: "activate",
});
const connections = await dalp.api.bankRail.connectionsList();The activation answers 200 with the connection:
{
"id": "0b7f5c1e-3d2a-4c8e-9f61-5a2e7d4b8c90",
"organizationId": "org_7Hq2kWm9",
"senderCompId": "DALP-ISSUER",
"targetCompId": "RESERVEBANK",
"reserveAccount": { "accountId": "AE070331234567890123456", "currency": "AED" },
"matchingMode": "reference",
"bankName": "Reserve Bank",
"allowedMessages": {
"inbound": ["credit", "balance-report", "execution-report"],
"outbound": ["payment-instruction"]
},
"payoutDeadlineSeconds": 604800,
"settlementWindowSeconds": 86400,
"holidayCalendarId": "adgm",
"paymentTimeZone": "Asia/Dubai",
"paymentCutOffTime": null,
"state": "active",
"credentialVersion": 1,
"rowVersion": 3,
"createdBy": "usr_4Fp8nQx2",
"createdAt": "2026-11-02T08:14:05.000Z",
"updatedAt": "2026-11-02T09:02:41.000Z"
}Every other route follows the same mapping: the operation names, such as cashOutCreate and reserve, are the SDK method names under dalp.api.bankRail, and the path gives the CLI command.
Set up a connection
A deployment serves the bank rail routes only when the DALP Helm chart value global. is true. It defaults to false, and the environment variable BANK_FIX_ENABLED also sets it, so the rail stays off until the operator turns it on. bankFix.mode defaults to simulator, which runs a FIX simulator inside the platform. The simulator listens on bank and bank, which default to 127.0.0.1:9878. If another process holds that port, the scheduler logs The bank FIX simulator cannot listen on <host>:<port>, so no bank connection can log on: <reason>, and its /readyz answers 503 with status bank- and the same reason. The simulator does not try again: free the port, then restart the scheduler pod.
POST /connections requires payoutDeadlineSeconds and settlementWindowSeconds. The Console fills in 604800 and 86400, which are 7 and 1 business days, because both count business time on the connection's holiday calendar. Each takes 60 to 31536000 seconds, and the window must be shorter than the deadline. The rail sends a payment instruction only while the deadline leaves at least the settlement window.
With bankFix.mode set to external, the platform dials your bank's FIX acceptor, the server end of the bank's FIX link, instead of running the simulator. The scheduler opens one FIX session per active connection to the bank's FIX acceptor at bankFix.external.host and bankFix.external.port. Every connection dials the same address, and the bank tells the sessions apart by the connection's two CompIDs. The settings take these variables:
With the rail enabled in external mode, the platform refuses to start in three cases. A missing host or port logs the external bank FIX mode needs bank. A tls of false for a host other than localhost, 127.0.0.1 or ::1 logs the external bank FIX session may only skip TLS to a loopback host. A reconnectMinMs greater than reconnectMaxMs logs bank. It never falls back to the simulator.
The scheduler checks the bank's certificate against its default certificate authorities and presents no client certificate. The scheduler pods need outbound access to the bank's host and port. One scheduler pod dials the bank at a time, and its /readyz answer carries a bankFix object: sessions counts the active connections it dials, loggedOn counts those whose Logon the bank has answered, and status is logged-on when the two match and not-logged-on otherwise. The other pods leave the object out, and so does the dialling pod while no connection is active. A session that is not logged on leaves the pod ready, with 200 and status ok, because the scheduler keeps dialling on its own, and a restart fixes neither a bank outage nor a refused logon. The scheduler logs The bank FIX session to the bank ended; reconnecting. with the connection id and the reason.
Each session follows the profile in Bank FIX session. A suspended connection gets a Logout within about 10 seconds and is not dialled again. A connection activated later is dialled within about 10 seconds.
Bank FIX session
- The scheduler connects and sends a Logon with
49set to the connection'ssenderCompId,56to itstargetCompId,108=30and the logon password in554. It never asks for a sequence reset. - The bank answers with its own Logon, with
49and56swapped and no554. A Logon numbered below the one the scheduler expected, without43=Y, is refused with a Logout naming both numbers, so the bank must keep its sequence numbers across its own restarts. A Logon with141=Yrestarts both sequences, and messages the scheduler stored earlier can no longer be resent. If no Logon arrives within 30 seconds of the dial, the scheduler closes the socket and dials again. - The scheduler sends a Heartbeat after 30 seconds without sending anything, and answers each TestRequest. It ignores the bank's
108and sends no TestRequest of its own: when the bank sends nothing for 90 seconds, the scheduler sends a Logout with the reason and closes. - After any close, the scheduler waits before it dials again:
reconnectMinMsthe first time, then twice as long after each attempt, up toreconnectMaxMs. An attempt that lasted at leastreconnectMaxMsfrom its dial starts the wait atreconnectMinMsagain. Each close logsThe bank FIX session to the bank ended; reconnecting.with the reason and the attempt number. - Sequence numbers in both directions are stored, so a reconnect or a restart carries on from them. When the bank's Logon carries a number higher than the one the scheduler expected, the scheduler sends a ResendRequest from the expected number to
0, the end of the bank's stream. A later gap gets a ResendRequest from the expected number to the one before the message that arrived. When the bank asks for a resend, the scheduler sends its stored credit acknowledgements, reversal acknowledgements and payment instructions again with43=Yand the original sending time in122, and covers its session messages with gap fills,35=4with123=Y. The scheduler keeps the newest 4096 of those messages it sent, up to 16 MiB, for each connection. A resend of an older one gets a gap fill. - A change of the logon password ends the session within about 10 seconds. The next Logon carries the new password.
Link the reserve token
A connection links one token for its reserve currency. A token is linked to one connection.
GET /connections/ lists the tokens this connection can link, each with token, name, symbol and decimals. A token is listed when step 1 below would accept it: the connection has no link, no connection of the organisation links the token, the token has no supply, its decimals cover the currency and the rail wallet can mint it. The list is empty for a connection that already has a link.
POST /connections/with{connection Id}/ token- links token(the address) andcheckerThreshold, an amount in the currency's minor units. While thebank-railapproval family is on, a cash-out waits for a second manager when it, added to the client's other cash-outs on the link from the last 24 hours that were not released, comes to more thancheckerThreshold. An optionalcreditCheckerThresholdholds large credits for approval before they mint, as Approve a credit before it mints describes. Creating the link is refused for a token that already has supply. The link startspending-roleand changes nothing on the token.- Optionally,
POST /connections/with no body runs the checks activation runs, signs nothing and asks for no wallet verification. It answers{connection Id}/ token- links/ {token Link Id}/ activation/ preflights 200with the nextaction:grant-supply-role(booked) oradd-supply-delegate(delegate) when activation would grant the authority,activatewhen the rail wallet already holds it,reinstatefor a suspended link that passes its checks, andnonefor an active link. Otherwise it answers the same422bankthat activation would answer, with the sameRail. token Link. REFUSED data.reasonanddata.accounts. Call it before you ask for wallet verification, and poll it after a grant until it answersactivate. POST /connections/with wallet verification. Your wallet grants the rail wallet the token's supply authority, so you need the token's admin role in{connection Id}/ token- links/ {token Link Id}/ activation bookedmode or its governor role indelegatemode. The call returns the link stillpending-rolewhile the grant settles. Call it again once DALP sees the grant on the chain, and the link becomesactive. It is refused when the token has supply, when any other account holds supply authority (another supply management role holder or supply delegate, and in delegate mode any role holder at all), or when the rail wallet has no gas or cannot hold the token. The checks stop at the first failure, so a refusal names one cause and, for holders, the accounts. With reasonsupply-role-held, a token admin revokes the role from the accounts indata.accounts. Withsupply-delegate-held, a token governor removes them as delegates.DELETE /connections/removes a link that is still{connection Id}/ token- links/ {token Link Id} pending-roleand has no clients and no open mint-limit request or request to let other accounts manage supply. Anactiveorsuspendedlink is refused with reasonstate.
On a booked link, send { "transition": "update- to the token link transitions route to let accounts other than the rail wallet that hold the token's supply management role book movements. The link reads allowOtherSupplyManagers (off at creation), supplyManagers (the other holders, read from the token, each with the time the grant was indexed) and supplyManagersRequest while a request is open. Turning it on opens a bank- approval that another bank rail manager decides on the decisions route. A second change while one is open is refused with reason supply-managers-pending. The transition on a delegate link is refused with reason supply-managers-delegate. While the bank-rail approval family is off, turning it on still needs wallet verification, applies at once and stores a waived record. Turning it off applies at once. While it is on, activation, re-activation and the pre-mint check require only that the rail wallet still holds the role. The reconciliation reserve line explains a payout another account booked before the bank paid as bookedAheadOfPayment until the payment settles. A cash-out another account booked reads that account as bookedBy. When the bank then does not pay, a critical operations case names the account and the amount.
The link's supply mode is fixed at creation from the token's features:
Both modes need a DALP custodian token that the organisation issued. Any other token is refused. The token's decimals must represent every minor unit of the currency exactly, so a EUR token needs at least two decimals. A token that fails these rules is refused with data.reason not-custodian, not-issued, needs-hold, redemption-, needs-supply-delegate or decimals. A token another connection links is refused with token-linked, a second token on the connection with currency-linked, and a token with supply with supply-not-zero.
Add clients and bank accounts
POST /connections/links a participant and one of its wallets. On a{connection Id}/ client- links referenceconnection DALP generates the payment reference. On avirtual-ibanconnection sendvirtualIban.DELETEon a client link removes it, and is refused while the client has a proposed or active bank account (accounts-live) or a credit still delivering to its custody gateway (deliveries-open).POST /connections/proposes a payout account: an IBAN, or an account number with the bank's identifier, the holder name and the bank. A participant proposes one for their own client link at{connection Id}/ client- links/ {client Link Id}/ accounts POST /users/.me/ bank- accounts POST /connections/takes{connection Id}/ client- accounts/ {account Id}/ decisions { "action": "approve" }, orrefuseorrevokewith areason. The manager who proposed the account cannot approve it. A refused or revoked account stays listed with its reason. Revoking releases a cash-out the bank has not been instructed to pay.
GET /connections/ lists the client links of a connection. GET /connections/ lists every account of the connection, and GET .../ reads one. A participant lists their own accounts at GET /users/. Each list pages with page.
A cash-out pays only to an active account.
A bank rail manager can make the reads that setup needs without also holding the system manager, identity manager or claim issuer role. GET /api/ lists the organisation's members as person participants beside the asset and add-on participants. GET /api/ lists the wallets of a member's participant, and an entity participant's wallets cannot be listed. GET /api/ reads the identity of the organisation's own rail wallet. Registering that identity stays with an identity manager.
Follow and resolve credits
GET /connections/ lists every credit the bank reported, newest first, and GET .../ reads one. A credit mints once to the matched client. When it matches no client or the token blocks the wallet, it mints to the rail wallet instead and waits there as suspense, a balance held for a manager's decision. A credit that cannot complete is parked, and each parked credit has an operations case, the record of a problem that the platform cannot resolve automatically.
POST .../ takes one of these bodies:
A manager other than the requester decides the open request with POST .../ and { "action": "approve" } or "reject" with its approvalId. A refused request or decision answers 409 bank, and data.reason names the cause, for example proposer-cannot-approve when the requester tries to decide it. While the bank-rail approval family is on, an allocation or a return puts the credit in awaiting-checker. With it off, the resolution starts at once. Approving moves or burns the tokens exactly once. Rejecting leaves the credit parked. In delegate mode an allocation is a plain transfer from the rail wallet, and a return burns through the supply delegate.
Switch a client to gateway delivery
A client link delivers to the client's wallet until two bank rail managers switch it. The link read carries delivery: the target (wallet or gateway), the open request (or null), the lastCheck and the rowVersion.
POST /connections/takes{connection Id}/ client- links/ {client Link Id}/ transitions { "transition": "enable-. The rail reads the client's gateway and checks it, stores each check by name ingateway- delivery", "row Version": 3 } lastCheck, and opens a delivery request that another bank rail manager decides. The credits keep minting to the wallet until then. A gateway that fails a check is refusedpreconditions-failed, and each failed check names the fix. A token in booked mode (booked-mode), a request already open (delivery-request-open) and arowVersionthe link has moved past (row-version) are refused too. When the gateway cannot be read the call answers a retryable503.POST /connections/takes{connection Id}/ client- links/ {client Link Id}/ decisions { "decision": "approve", "approvalorId": "... " } "reject"with an optionalreason. The manager who opened the request cannot decide it (same-actor). An approval reads the gateway again: when every check still passes,delivery.targetbecomesgateway. Otherwise the request is withdrawn and cleared, a warning case is raised and the approval is refuseddelivery-stale. A rejection keeps delivery on the wallet. A request nobody decides in seven days closes the same way.- The same
transitionsroute with{ "transition": "disable-sends the client's future credits back to the wallet at once and withdraws an open request. Credits whose mint already settled keep delivering.gateway- delivery", "row Version": 4 }
Credits for a custody gateway client
A credit for a client that takes delivery through a custody gateway mints to the rail wallet, never to the client's wallet, and the credit reads minted with a delivery object. Before minting, the rail checks the gateway. A failing check parks the credit as delivery-blocked with nothing minted, and the case names the check. Fix the gateway, or switch the client to wallet delivery, then send { "action": "retry" }. When the gateway cannot be read, the credit stays admitted and the rail tries again on its next pass.
delivery is null for a credit that mints to the client's wallet. Otherwise it names the gateway and its receiptToken, the lastCheck the rail read before minting, and state. state is null while the mint is being submitted and minted-undelivered once the coins wait in the rail wallet to be sent, with amount in the token's smallest units. Such a credit counts in the reconciliation as inTransit, not as suspense. It cannot be allocated or returned to the payer while it is delivering, and it cannot be recalled while its send is being submitted.
The rail then sends the coins from the rail wallet to the gateway, after checking the gateway again. While the send is submitted state reads sending. Once it is mined it reads sent-unreported, with the transactionHash and logIndex of the transfer. A gateway the rail cannot read, or one that is only paused, holds the send for the next pass under a screening-outage case, and no attempt is spent. Any other failing check, a gateway that changed since the mint, or a send the token refuses, that is dropped or that reverts, leaves state at send-failed with a critical case.
Send { "action": "retry" } to send a send-failed credit again under its next attempt. The credit's attempts counts them. The rail first reads the failed send. When its transfer to the gateway landed after all, the credit is recorded as sent-unreported and nothing is sent twice. The retry is refused with 409 RETRY_REFUSED, and data.reason says why:
delivery-in-flight: the last send may still land.recall-open: the bank's recall of the credit is open.returned-elsewhere: the gateway paid the coins back to an address other than the current rail wallet, so they cannot be sent again.delivery-not-retryable: the delivery is in no state a retry can change.
Where the coins are
The rail follows a sent credit to the gateway's deposit, so delivery.state tells you where the coins are. It looks the deposit up by the transactionHash and logIndex of the send, then by its depositId. The read also carries gatewayStage, the gateway's gatewayStages with their transaction hashes, and, once the gateway accepted the credit, receiptsMinted, fee and receiptRecipient. The same deposit appears in the gateway's own records as custody..
sent-unreported: the gateway's keeper has not reported the deposit yet. After 60 minutes the credit readsunattributedand a warning case names the usual causes: the keeper is off, the coin is unsupported, the sender is not a funding source, or the transfer shared a transaction with a deposit event. The rail keeps reading.quarantined: the gateway holds the deposit for screening. After 24 hours ascreening-freezewarning case opens.refused: the gateway's receiving rule refused the mint and holds the coins in escrow. A critical case opens at once and names the cause. After the connection's settlement window an overdue case follows. A gateway manager recovers the deposit to the rail wallet.settled: the gateway accepted the credit. Every case of the credit is resolved.returned: the gateway paid the coins back, toreturnedTo. When that is the rail wallet,retrysends the credit again with the returnedamount. Otherwise a critical case names the address andretryis refused withreturned-elsewhere.
A gateway read that fails changes nothing and opens no case.
Approve a credit before it mints
When the token link has a creditCheckerThreshold, the rail adds each matched credit to the client's credits of the last 24 hours before it mints. A credit that matched no client is judged on its own amount. While the bank-rail approval family is on, a credit above the threshold shows awaiting-mint-approval with mintApproval, which carries the approvalId and its expiry, seven days after the request. It mints nothing until a bank rail manager approves it.
- Approve with
{ "action": "approve", "approvalon the credit's decisions. The bank's message opened the request, so any bank rail manager can decide it. The credit mints the next time the rail processes credits. A completed wallet rotation drops the approval of every credit on the old wallet that has not minted, because the approval named that wallet, so such a credit is judged again before it mints. A credit the bank is recalling cannot be approved.Id": "... " } - Reject with
"reject". A rejected or expired request parks the credit asmint-unapprovedwith a critical case.retryasks again with you as requester, so another manager must approve it.return-to-payercloses it without a transaction. - A credit awaiting approval counts in the reconciliation's
pendingInand shows asawaitingApproval. It is never overdue.
To change the threshold, send POST /connections/ with { "transition": "update-. Setting a first threshold or lowering one applies at once. Raising it, or removing it with null, returns the link with mintLimitRequest and keeps the old value in force. mint names the manager who asked, and a different manager decides. Another bank rail manager decides with POST .../ and { "decision": "approve" } or "reject" with the request's approvalId. A rejected request, or one that expires after seven days, keeps the old value. While the bank-rail approval family is off, the raise still needs the manager's wallet verification, applies at once and stores a waived record instead of a request. A second change while one is open is refused with reason mint-limit-pending. A new threshold applies to credits the rail has not judged. A credit already cleared under the old one still mints.
A delegate link can also pin the cap the rail wallet mints under, so a stolen rail wallet key cannot mint past it. Pass expectedMintCap, in the token's smallest units, when you create the link, and activation adds the rail wallet with that cap. Without expectedMintCap the rail wallet mints without a limit. A booked link takes no cap, and a cap on one is refused with reason mint-cap-booked, because its rail wallet holds the token's supply management role. For those links, containing the rail wallet is the control after a key exposure.
To change the cap later, send { "transition": "update- to the same transitions route with wallet verification. Your own wallet removes the rail wallet as supply delegate and adds it again with the new cap, so you need the token's governor role. The rail wallet's re-delegation is a queued transaction, so the answer follows the Prefer contract of the async settlement guide: 200 when the write landed within your Prefer: wait, otherwise 202 with the link, its mintCapReplacement (transactionId and previousMintCap) set, and Location pointing at GET /connections/. Reading that list settles the replacement against the transaction queue, and so do credit processing, reconciliation and the rail wallet rotation readiness check: it clears mintCapReplacement when the write lands and puts expectedMintCap back to previousMintCap when it fails. A further cap change while mintCapReplacement is set is refused with reason state. Read the list, then change the cap again.
A first, lower or unchanged cap applies at once, and credits park as supply-authority until DALP sees the new delegate on the chain. Raising the cap, or removing it with null, opens the same mintLimitRequest, and the approving manager's wallet adds the delegate with the new cap. A cap change during an open rail wallet rotation is refused with rail-wallet-contained. A delegate link lists outstandingMint, the units the rail wallet has minted minus those burned, beside expectedMintCap, as of the last indexed block, so a client can size a raise before it asks. It is null on a booked link or when the rail wallet is not an indexed delegate. A credit the token refuses because it would pass the cap parks as mint-cap, and its case names the cap, the amount outstanding and the credit's amount. Raise the cap or wait for burns, then retry it.
Screen credits and cash-outs
Every confirmed deposit, including an unmatched deposit, and every cash-out before its payment instruction, is sent to the AML provider the connection names, as a fiat transaction with its direction, amount, currency and counterparty. The rail records the provider's verdict and alerts on the credit (screening) and on a screening row you can list. Each decision also creates a case linked to the investor, with the verdict and alerts. An unmatched deposit uses its transaction reference as the provider's external subject until the investor is identified. The payer of a credit is on the credit as payer: the name, account and BIC the bank sent in the FIX deposit message's tags 5001, 5002 and 5003.
GET and PUT /connections/ read and replace how a connection screens:
A credit that fails a check parks, and nothing mints:
payer-: the payer account is not an active account of the client, or the bank sent none.account- not- verified aml-rejectedandaml-held: the provider's verdict. A score without a verdict reads as held at or above the provider's auto-clear threshold.
For payer-, add the payer account and have a different manager approve it before retrying. A mint approval cannot bypass this check. For an AML hold or rejection, retry asks for a mint approval with you as requester, and another bank rail manager approves it with POST .../. The approval is never waived, whatever the bank-rail approval family says, and it records who released the screening. A cash-out the check rejects or holds goes to awaiting-checker with an approval naming the original requester as maker, which a different bank rail manager decides with POST .../.
GET /connections/ lists the screenings of credits and cash-outs, newest first, with filter[from] and filter[to] as ISO date-times on when the rail first screened the transaction. A bound that is not a date-time answers 400 wire.. Each row carries the counterparty, payerCheck (verified, unverified or not-required), verdict (not-screened, pending, approved, held or rejected), the score, the provider's alerts, the provider's reference, the submission and decision times and who released it.
Recalls
When the bank reverses a credit, it sends a credit reversal over FIX and the rail records a recall against the credit. Add credit-reversal to the connection's allowed inbound messages with PATCH /connections/, or the session ends on the first reversal.
GET /connections/ lists the recalls, newest first, and takes filter[state]. GET .../ reads one, or answers 404 when the connection has no such recall. Each recall names its credit, the bank's reversal id, the amount the bank reversed, the amount in token units and its state. A new recall is received: the credit no longer mints, a retry of it is refused, and a critical operations case names the recall.
The rail refuses a reversal when the credit already has an open recall, when its completed recalls cover its full amount, or when the reversal would take back more than the credit. It records nothing and raises a critical case naming the reversal. A reversal of a credit the rail has not seen yet gets no reply, so the bank's resend is recorded once the credit arrives.
Execute or abandon a recall
A recall takes tokens back only under maker-checker. Post the transition to POST .../:
{ "transition": "request-asks another bank rail manager to approve taking the tokens back. The rail works out what it will do from the credit: cancel a credit that has not minted, burn or book from the rail wallet for suspense, wait for a custodian's forced transfers from a delegate client, book a correction from a booked client, or, for a credit the client's custody gateway holds (sent but unreported, unattributed, quarantined, refused or settled and awaiting custody), wait for the gateway to return the deposit to the rail wallet. A credit minted and still unsent, or whose send failed, burns from the rail wallet and lowers the amount still to send, cancelling the delivery when nothing is left. The request is refused, naming the blocker, while the credit's mint is queued, the credit's send to the client's custody gateway is being submitted (execution" } delivery-in-flight), a park resolution is open, a live cash-out holds the client's tokens, or the rail wallet is contained. A credit settled into receipts is recalled through its receipts, as Recall a delivered credit describes. While the recall is open, a cash-out of that client's tokens is refused withdata.reasonrecall-openand can be requested again once the recall closes.{ "transition": "abandon", "reason": "...asks another manager to approve ending the recall without taking anything back. The bank's debit stays a reserve difference until a manager records a reserve adjustment." }
Either moves the recall to awaiting-checker. Decide it with { "action": "approve", "approval or "reject" on POST .../. The requester cannot decide it. Approve recomputes what the request bound and is refused recall-stale when the credit moved in between. The recall then returns to received with a case. Reject, or an approval that expires, also returns it to received.
An approved delegate recall is clawing. A token custodian force-transfers the tokens from the client into the rail wallet, the staged wallet during a rotation, and a manager records each transfer with { "transition": "record-. A transfer made before the approval, of another token, from another wallet or into another wallet is refused claw-mismatch, and one another recall already recorded is refused claw-duplicate. A transfer in a block the chain has not finalised yet is refused claw-unfinalized. Record it again once the block is final. Recording the same transfer again on the same recall returns the recall unchanged. The recall read lists the recorded claws and what the client still holds, free and on the rail's escrow. Claws reaching the recall amount, or { "transition": "close-, move the recall to burning: the rail burns what it recovered once, writes down the rest and completes the recall, then returns any excess to the client. When the client's wallet refuses that transfer, the excess stays in the rail wallet under a warning case and the rail retries every five minutes. A booked recall books one correction from the client to zero against the line that minted the tokens. For a credit the gateway holds, a manager of the gateway returns or recovers the deposit to the rail wallet, and record-claw takes that exit's transaction hash and log index. The rail checks that the gateway reports the deposit returned or recovered, that it paid the rail wallet and that the exit's transfer is mined and final. A payment to another wallet is refused claw-mismatch. It records what was paid, up to the recall's amount, and lowers the amount the credit still sends by the same.
Recall a delivered credit
A credit whose delivery settled reads delivery.state settled: the client's custody gateway took the coins and issued receipts for them, which are another token. The rail cannot burn receipts, so it takes them back from the client and turns them into coins at the rail wallet's own custody gateway, then burns the coins. The bank's amount minus what the gateway pays out is written down, so the gateway's deposit fee and withdrawal fee end in the write-down.
Set the rail wallet up as a client of a custody gateway before you need a recall. request-execution on a settled credit is refused with 409 (DALP-14137) and data.reason recall-gateway-missing when the current rail wallet cannot redeem there. That happens when it has no on-chain identity, its identity has no custody gateway, it is not a client of that gateway, or it is not an active approved payout address on it. The refusal opens no approval. A gateway manager fixes the named step, then you send the request again. While the rail has not read which wallet holds the receipts, the request is refused delivery-in-flight. Send it again once the delivery read shows receiptRecipient.
Once the recall is approved it is clawing, and its receiptToken names the receipt token it takes back. A custodian of the receipt token force-transfers up to the recall amount of receipts from the wallet in delivery. into the rail wallet, and you record each transfer with record-claw. A transfer of the link's coin, of another token or from another wallet is refused claw-mismatch. When the client has moved some receipts away, record what the custodian could take and send close-claw: the rest is written down.
In burning the rail redeems the receipts from the rail wallet. It approves the gateway's escrow for the receipts, requests the redemption on the rail wallet's gateway with the rail wallet as receiver, and takes the request id from that transaction. The recall read shows redemption with the gateway, the rail wallet, the request's stage, its requestId, the receiptUnits it redeems and the paidUnits the gateway paid. The gateway handles the request as any withdrawal: its managers review it, screening answers when the gateway screens, and the custodian funds the gateway. The rail follows only its own request id. When the request becomes claimable, because auto-claim is off or the keeper's payout failed, the rail claims it. Once the gateway has paid the rail wallet, the rail burns the coins paid, at most the receipts it recorded, and the recall completes.
A request the gateway freezes, holds for a payout address, rejects or pays to another wallet burns nothing. redemption.stage reads failed, the recall stays burning and a critical operations case names the gateway step. Fix it on the gateway, and the recall completes on the next reading that shows the request paid to the rail wallet. Once a rejected request has returned its receipts to the rail wallet, redemption.stage reads approving again and the rail requests a new redemption of the same receipts after five minutes, for the gateway's managers to review. When the rail wallet's gateway becomes unusable while the recall is burning, a critical case opens and the rail tries again after five minutes.
A completed recall reads recalled with recoveredUnits, writtenDownUnits and the burn's txHash. The write-down is a credit-recall reserve adjustment the rail creates itself, effective at the reversal. A manager cannot propose one. The credit reads recalled once its recalls total its amount, and recalledAmount otherwise.
Pay out a cash-out
A cash-out instructs the bank to pay a client's active account, then burns the held tokens when the bank reports the payment settled. A rejected or late payment releases the tokens to the client. The rail builds each instruction once, and the instruction id is the cash-out's id, so a resend carries the same id for the bank to recognise.
Booked mode
The client's wallet requests a redemption on the token, which holds the tokens on a redemption line. A manager lists the open lines with GET /connections/, then records the cash-out with POST /connections/ and a lineId and a clientAccountId. A participant records their own at POST /users/ with connectionId, lineId and clientAccountId. Sending the same line and account again returns the stored cash-out. The same line with another account is refused with 409 (bank).
Both routes, in both supply modes, refuse a cash-out with 409 bankRail.cashOut.REFUSED and data.reason participant-restricted while the client's participant account is suspended, closed, terminated, or restricted with the withdrawals restriction. Other restrictions do not block a cash-out. A checker's approve is refused the same way, and reject still works. A cash-out approved before the restriction is released with reason participant-restricted before the rail sends its instruction or locks its tokens.
Delegate mode
A delegate token has no redemption line, so an escrow holds the tokens.
- A bank rail manager who also holds the escrow manager role creates the escrow with
POST /connections/and wallet verification. The caller's own wallet signs the factory call. Another caller gets{connection Id}/ token- links/ {token Link Id}/ escrow escrow-manager-missing, a system manager included. Any bank rail manager reads the escrow withGETon the same path. The rail wallet is the escrow's controller. The route honoursPrefer: it answers200with the statereadyand the escrow's address when the creation was indexed within yourPrefer: wait, otherwise202with the statecreating, the transaction when it is known, andLocationset toGET .../. Poll that read until the state isescrow ready. Sending the request again re-attaches to the same creation. The route is refused while the rail wallet controls any escrow, and cash-outs are refused while it controls none or more than one. - The client approves the escrow, from the linked wallet, to take at least the amount.
- The client records the cash-out at
POST /users/withme/ bank- cash- outs connectionId,amountandclientAccountId, and anIdempotency-Key. The manager route accepts a line only, so a manager cannot record a delegate cash-out for a client. - The rail deposits the tokens in the escrow and locks them until the payout deadline. It locks them again with a new deadline after a checker approves.
The request answers while the cash-out is still locking. Once the lock settles, it moves on to awaiting-checker or approved. To wait for that move, follow the cash-out's stream, as Follow a cash-out describes.
When the bank reports the payment settled, the rail pays the lock to the rail wallet and burns it through the supply delegate. Otherwise it unlocks the tokens, and the client withdraws them from the escrow. A request is refused when the client's approval or balance does not cover the amount. When the approval drops before the rail moves the tokens, the cash-out is released as not-locked and an operations case says what to do.
Read and decide
GET /connections/ lists cash-outs and takes filter and page. GET .../ reads one. A participant lists their own at GET /users/. A cash-out above the threshold always shows awaiting-checker with an approvalId. A different manager sends POST .../ with { "action": "approve" | "reject", "approval. Approving sends the instruction. Rejecting cancels the redemption line or unlocks the tokens.
Follow a cash-out
GET .../ sends the cash-out as server-sent events, with the same role and errors as the read. The first event is the cash-out as it stands. After that, an event arrives each time the cash-out changes. The stream ends after the event of a cash-out the rail has nothing more to do for on its own: paid, released, returned, or unbooked with no recovered units left to burn. Otherwise it ends once 180 seconds pass without a change. Open it again to keep following. A cash-out that already rests sends one event and ends.
A bank return after paid, a late settlement after released or a recover on unbooked moves a cash-out on again. After one of those, open a new stream to follow it. In the SDK the stream is bankRail.cashOutStream.
Bank hours and value dates
A connection carries holidayCalendarId, paymentTimeZone and paymentCutOffTime. Left out on create, they default to adgm, Asia/Dubai and null. Left out on PATCH, they keep their value. holiday makes every day a business day. payment means the whole business day takes payments, and 00:00 is refused. A calendar that is neither a base calendar nor the organisation's own answers 422 with bank.
A cash-out keeps the settings it was requested under and answers paymentCutOffTime, paymentTimeZone, expectedValueDate and releaseAt. A cash-out approved at or after the cut-off, or on a closed day, stays approved with releaseAt at the next opening. deadlineAt counts business time from that opening, and closed days never push it more than a year past that point. A closure added later moves releaseAt and expectedValueDate, and releases the cash-out as expired when the deadline no longer leaves the settlement window.
After the rail builds an instruction, the FIX session can resend the stored frame on any day, marked as a possible duplicate.
GET /api/ answers payoutTiming with the cut-off, the time zone and the value date a cash-out approved now would get. The reconciliation read names report.businessDay and suspense.
Returned payouts
The bank may return a payout it already settled, for example when the destination account was closed. The rail records each return in the cash-out's returns with the bank's returnId, amount, valueDate and the creditId of its re-credit. A paid cash-out shows returning until every return has a re-credit, then returned.
A re-credit is a credit like any other on GET /connections/, with return:<cash as bankTxId and the cash-out in returnedCashOutId. Whatever the link's credit threshold, it asks for mint approval: while the bank-rail approval family is on, it waits in awaiting-mint-approval for a manager's approve on its decisions route, as Approve a credit before it mints describes. With the family off, it mints at once. A parked re-credit cannot be returned to the payer, because the bank already refunded the payee.
A return that arrives before the bank reports the payout settled, while the cash-out is instructed, releasing or released, is held in returns with no re-credit and a warning case, and re-credits once the late settlement is booked. A return of a cash-out whose instruction never reached the bank, in another currency, or above what is left of the payout records nothing and raises a critical operations case to settle with the bank.
If a released cash-out settles late, its webhook status stays released during recovery. Once the rail recovers and burns the full amount, it sends bank-rail.cash-out.paid, including when a pending return starts re-crediting immediately. bank- follows when the re-credit is admitted. Its credit's minted event reports the completed mint separately.
Contain the rail wallet
The rail wallet is the key that mints and burns for every bank connection of the organisation on a chain. A custody provider configuration of the deployment holds that key, and the platform signs the rail's mints and burns with it, with no manager signature. When that key is lost or exposed, a manager contains the wallet so the rail signs nothing more with it.
While the wallet is contained, a credit parks as supply-authority and mints nothing. A cash-out step the wallet would sign waits with one operations case and does not count as a failed attempt. A cash-out that is not instructed is released with reason link-inactive. Activating or removing a token link is refused with reason rail-wallet-contained.
An approved abandon makes the wallet active again, reinstates the token links the containment suspended that still pass their checks, and resolves the waiting cases. A rejected or expired request returns the rotation to contained. A compromised rotation is refused with 409 and reason compromised, because a key someone else may hold must not sign again.
Containment stages the next rail wallet. To move a token link to it, revoke the old wallet's authority and then grant it to the staged wallet, one token link at a time. You sign both with your own wallet, so the rail never changes who may mint. Each step changes nothing when it is already done, so a retry is safe. Supply authority is the supply management role for a booked link, and the supply delegate entry for a delegate link.
The grant is refused while any account still holds the authority (422, reason supply-role-held or supply-delegate-held with the holders), and when the staged wallet cannot hold the token or has no gas. On a compromised rotation it also waits until no account but the staged wallet holds authority on any token link of the rotation (409, reason revoke-pending) and its escrow holds no tokens (409, reason old-escrow-funded), because whoever holds the old key controls both. The first revoke or grant a manager submits sets handoverStartedAt on the rotation. One the token refuses before anything is queued, such as from a manager without the role, leaves it as it was. From then on, and whenever any authority has left the old wallet, an abandon request or approval is refused with 409 and reason authority-moved, because the old wallet may no longer hold what an abandon would hand back to it. While the rotation is open, a delegate link's escrow routes read and create the escrow of the staged wallet.
Complete the rotation when every token link has moved. The request is refused with 409 and reason completion-blocked, and the message names every blocker, while any of these is true:
- The old wallet has transactions queued that have not reached a final state.
- The staged wallet does not hold a token link's supply authority alone.
- A delegate link has no escrow that the staged wallet controls.
- The old wallet's escrow still holds tokens.
- The old wallet holds tokens and is not frozen on that token.
- The staged wallet holds less of a token than the connection holds in suspense.
- A grant of supply authority to the old wallet is still queued, submitted or waiting for approval on a token linked to it. The message names the token and the request. This includes the re-delegation of a delegate link's mint cap that has not settled.
- The old wallet holds supply authority on any token linked to it, whatever the rotation's reason, including a link that still waits for activation. Revoke it with
revoke-oldon a suspended link, or ask a token admin or governor to revoke it on a link waiting for activation. - The indexer is behind the chain, so the platform cannot tell whether a grant landed. Request completion again after a moment.
While a grant to the old wallet is in flight, the refusal also raises one operations case of kind security for the rotation, with severity critical for a compromised rotation and warning for a lost one. The case resolves when the rotation completes or is abandoned.
The rail stops accounting for the old wallet after the switch, so its tokens must be gone or frozen first. When a second manager approves, or on the next read of the rotation while the bank-rail approval family is off, the rail checks the blockers again and then switches in one step. Token links and credits move to the staged wallet, and a credit parked as supply-authority since the containment is admitted again. The staged wallet becomes active and the old one retired, and the rotation becomes complete. The rail then reinstates the token links that still pass their checks and resolves the waiting cases. A rejected or expired request returns the rotation to contained.
Between the approval and the switch the rotation is approved. If the switch fails, for example because the custody binding cannot move, the decision returns 409 with reason state and the rotation stays approved. Do not decide again: the next read of the rotation or the rail wallet tries the switch again.
If a key reported lost turns out to be exposed, send the contain request again with compromised. The open rotation becomes compromised and any pending request to abandon it is cancelled. A request to complete it stays open. A rotation never goes back from compromised to lost.
Empty an escrow the old wallet controls
In delegate mode the old rail wallet still controls the escrow, so the rail cannot unlock or pay its locks. A wallet holding the token's custodian role moves them with POST /api/, with wallet verification and an Idempotency-Key header:
{
"token": "0x…",
"from": "0x…holder",
"to": "0x…",
"amount": "1000000",
"lockIds": ["0x…"]
}The escrow takes the holder's free balance first, then the listed locks. For a lock that backs a cash-out, to depends on the cash-out:
A refusal is 409 ESCROW_ with data.reason, and a wallet without the custodian role gets 403 ESCROW_. Locks of paid and unpaid cash-outs go in separate requests. POST /api/ refuses an escrow as from.
The Platform API applies these rules, and the escrow contract applies none of them. The contract checks the caller's custodian role on the token and nothing about cash-outs, so a custodian who calls the escrow directly can draw any of the holder's locks, including one behind a cash-out the bank has been instructed to pay, and pay any recipient the token accepts. Send every forced transfer from a rail escrow through this route.
Recover reads the forced transfers of the cash-out's lock to the rail wallet in blocks the chain has finalised, records them in the cash-out's recovery, and burns those units from the rail wallet. The cash-out shows paid once the burnt units reach its amount. Sending recover again before a new forced transfer returns the cash-out unchanged, including once it shows paid. When every transfer is past the finalised block, recover is refused with reason recovery. Send it again once they are final.
Reconcile the reserve
GET /connections/ compares the bank's newest balance report with the token's supply, and the rail wallet's balance with the credits it holds as suspense. The token facts must come from an unchanged drained block at or beyond the chain's finalised block. Facts are unavailable while that watermark is missing, moves during the read, or has not reached finality. The rail keeps that report pending and retries it once the facts are available. A value the platform cannot read is null, never zero. Send the token's chain in the X-Chain-Id header.
A recall the bank sent by the report's time and still open explains the reserve line as recalledNotBurnt. Once it has stayed open longer than the settlement window it moves to overdueRecalls, no longer explains the line, and raises its own critical case. A recall the bank sent after the report's time that the rail already completed explains it as recallBurntAfterReport, less its write-down. Clawed tokens in the rail wallet waiting for their burn, or for an excess to go back to the client, explain the suspense line as awaitingBurn. Credits the rail minted to its wallet for a client's custody gateway and has not delivered yet explain it as inTransit. The suspense line also shows terms that enter no coin line: inGateway (coins that reached the gateway and wait there, including a refused deposit in escrow), delivered (coins the gateway accepted and the receipts it issued for them, the receipts being another token), returnedElsewhere (deliveries the gateway paid back to a retired rail wallet, also shown on that wallet's railWallets row next to its own inTransit), inRedemption (receipts a recall took back into the rail wallet that its gateway has not paid out in coins yet, with excess receipts the rail still owes the client. Once the gateway pays, its coins count in awaitingBurn) and deliveryBreakdown (the count and coins per delivery state). A credit that is inTransit while its transfer settles can lower the rail wallet balance before it leaves that term, so a short-lived difference clears on the next report. The daily reconciliation CSV has the columns inGateway, inGatewayCount, deliveredCoins, deliveredReceipts, deliveredCount, returnedElsewhere, returnedElsewhereCount, inRedemption and inRedemptionCount, then the coins and the count of each delivery state as delivery.<state> and delivery.<state>Count (a state with no credit shows 0). A version stored before they existed shows unavailable in all of them, never 0.
During a rail wallet rotation the line covers both keys. railWallets lists every wallet that can still hold the token, oldest generation first: the wallet the link is pinned to, the current wallet, the wallet an open rotation staged and every retired wallet. Each entry carries its state, balance and whether it holds the supply authority. rotation names the open rotation, or is null. The suspense line sums the listed balances, leaving out what a frozen retired wallet holds, so moving tokens from the old wallet to the new one changes nothing. The line breaks when an account other than a rail wallet holds the supply authority, when no listed wallet or more than one holds it, or when a retired wallet the token has not frozen still holds tokens. A frozen retired wallet's holding raises a warning case instead.
Daily reconciliation
A daily version preserves both reconciliation lines so you can reread or export the evidence a run used. Its line omits the live reconciliation's consecutiveBreaks and breakSince: those fields belong to the live report evaluator. Set the connection's reportCutoff to a time in HH:MM:SS form and an IANA timeZone. The default is 23:59:59 in UTC. The first run or scheduled attempt for a date pins that cutoff, even if you later change the connection's setting.
POST /connections/with{connection Id}/ daily- reconciliations { "date": "2026-creates a run for a closed civil date. It selects the latest bank balance report at or before the cutoff and current indexed token facts.10- 24" } pins.balanceReportAsOfnames the report's value time, andpins.indexedBlocknames the stable drained block read for the token facts. These facts are current when the run executes, rather than historical chain state at the day's cutoff.GET /connections/lists the latest stored version per date. Use{connection Id}/ daily- reconciliations filter[from]andfilter[through]for an inclusive date range,sort=dateorsort=-date, andpage[limit]andpage[offset]to page through it.GET /connections/reads the latest version. Add{connection Id}/ daily- reconciliations/ {date} version=1to read a particular version andformat=csvto export it. The CSV response containsfilename,contentType(text/csv) andcsv. Every amount has a named column, and unavailable amounts are blank. A stored version exports the same bytes on every read.
Each version carries runAt, its trigger and manager actor, the two-line line, and unavailableReasons. A retained report whose denomination differs from the current token link leaves the reserve line unavailable, with a reason. openCaseCount counts distinct unresolved reconciliation subjects of the connection, its token links and its daily reports at run time. It excludes cases for individual credits and cash-outs. A failed case read stores null and an unavailable reason, rather than a partial count. The CSV calls this column openReconciliationCases.
ledger is the general-ledger leg of the three-way reconciliation: the bank's reported balance, the token's supply and your organisation's Xero ledger. The reserve line ties the bank balance to the supply, and ledger ties the bank balance to the client-money account in Xero. The account is the one your accounting rules debit when a bank credit in the connection's currency arrives, read from the Xero Balance Sheet as at the end of the run's date in the Xero organisation's base currency. ledger.balance is that account's balance, ledger.accountCode names it, and ledger.unexplained is the ledger balance minus the bank's reported balance. ledger.status is clear when the two are equal, break when they differ, and unavailable when no bank report matches the linked token or the ledger could not be read. ledger is null when your accounting rules post no bank credit in the connection's currency, so the ledger is not part of that reconciliation.
A ledger that cannot be read adds an unavailable reason that says why: Xero is not connected, the connection does not reach the configured tenant, no Xero account is mapped to the client-money account, the ledger is kept in another currency, or Xero refused the read. The scheduled worker retries that day like any other unavailable evidence, and the next run that reads the ledger stores a new version. A Xero connection made before DALP asked for the accounting. scope is refused the read: connect Xero again to grant it. The CSV adds ledgerStatus, ledgerAccountCode, ledgerBalance and ledger.unexplained.
The report, connection settings, application totals and case count are read together. Token facts must belong to the same serving deployment at a stable finalised block whose committed work has drained, including a check after the application reads finish. A serving change, undrained work or a submitted token handoff with no recorded outcome leaves token amounts unavailable and the day eligible for retry. An ordinary pending credit that has not submitted its mint remains valid input.
Equal report and block pins return the existing version without writing. A higher pin with neither axis lower creates the next version. An older report time or indexed block is refused with 409 and data.reason evidence-older. A date whose cutoff has not passed is refused with date-not-closed. A cutoff that does not identify one instant, such as a repeated or missing clock time at daylight saving, is refused with cutoff-unavailable. A date or version that has not been stored returns 404.
With bank FIX enabled, the worker checks active and suspended connections every five minutes. It runs the last three closed days, skips complete versions and retries days with unavailable evidence. It retains each attempted closed date even when a rejected cutoff or failed read or write prevents a version from being stored. If an incomplete day leaves that window, it raises one warning reconciliation case naming the date and reasons. A date with no stored version still returns 404, and the platform invents no snapshot or CSV for it. A failed warning write is retried. You can still request a run for that date by hand.
Reserve adjustments
A bank fee, interest, a refund to a payer or the opening balance of a token moves the bank balance without a credit or a cash-out. A reserve adjustment explains it.
POST /connections/with{connection Id}/ reserve- adjustments reason(bank-fee,interest,refund-to-payeroropening-balance), a signedamountin token units, the bank'sbankReferenceandeffectiveAt. It is refused with409anddata.reasonwhen the connection has no linked token (no-token-link), the amount has more fraction digits than the token (amount-precision) or is too large (amount-too-large), the posting is in the future (future-posting), or a live adjustment already uses the reference (bank-reference-used). A zero amount fails request validation.POST .../withreserve- adjustments/ {adjustment Id}/ decisions { "action": "approve" }or"reject". The manager who proposed it cannot decide it, and the409carriesdata.reasonproposer-cannot-approve. While thebank-railapproval family is off, a new adjustment is approved at once and needs no decision.GET .../lists them.reserve- adjustments
Only an approved adjustment applied at or before a balance report's time explains that report. An adjustment approved later takes effect from the next report, and the break case clears when that report reconciles.
Next steps
- The Console guide shows the same flows on screen.
- The error index lists every bank rail refusal code.
- Idempotency and async settlement explains the
Idempotency-KeyandPrefercontracts.