Handle failures

For integration engineers who need a correct response to every way an operation can fail.

Who this is for

Integration engineers building the part of a bank's system that submits token operations and reacts to their outcome. This page covers how to detect a failure, how to tell whether any value moved, and what to do for each failure code.

Two kinds of failure

KindHow you see itDid anything happen?
Refused at submissionA 4xx response to POST /api/transactions. No operation is created.No.
Failed after acceptanceThe operation reaches FAILED. failureCode classifies why; reasonCode gives the specific reason where one was reported.Establish it from the operation. See the next section.

A refusal at submission is covered on Errors. The rest of this page is about operations that were accepted and then failed.

Did any value move?

A FAILED operation is terminal. Read two fields to see how far it got:

txHashblockNumberWhat it means
AbsentAbsentThe operation never reached the ledger. No value moved.
PresentPresentThe ledger transaction was included and reverted. No value moved. reasonCode usually carries the revert reason.
PresentAbsentA hash without a block number is not proof of settlement or final rejection. Read the operation and break evidence and reconcile with the operator before submitting a replacement.

Balances read current contract state. A known pre-broadcast refusal or a verified reverted transaction establishes no token effect. A terminal status or missing hash alone does not settle every broadcast uncertainty; reconcile the ledger and break evidence before reversing cash or issuing a replacement. See Balances.

Recover without repeating a payment

When the response or outcome is uncertain, keep the original instruction and establish what happened before authorising more work. The bank's cash exception and the token outcome need to be reconciled together.

%%{init: {"theme": "neutral"}}%%
flowchart TB
    Keep["Preserve the original instruction"] --> Read["Read its current token outcome"]
    Read --> Evidence["Establish ledger and break evidence"]
    Evidence --> Match["Reconcile cash and token records"]
    Match --> Next["Authorise the appropriate next action"]

A pending or unknown outcome remains an investigation. Reuse the original saved key and unchanged body when recovering a lost submission response. A new key represents a new instruction and could create a second payment while the first still settles.

View in API Reference

What to do for each failure code

Replaying a recorded failed operation with its original key returns that operation; it does not restart it. Address the cause and establish the original ledger outcome before authorising a replacement with a new key. The replacement passes through the configured policy, approval and signing path.

failureCodeWhat happenedWhat to do
VALIDATION_REJECTEDValidation or policy refused the operation before any ledger transaction was prepared.Do not resubmit unchanged. Find the rule or check that refused it, and resubmit only if the operation should now pass.
APPROVAL_REJECTEDAn approver refused it.Treat it as a business decision. A new operation goes through approval again.
POLICY_EXPIREDThe approval window closed before a decision.If the payment is still wanted, submit a new operation. Consider why approvers did not act in time.
POLICY_TIMEOUTThe vocabulary includes a policy-response timeout, but its deadline is not currently enforced.For an operation still POLICY_PENDING, escalate to the operator. Do not create a replacement merely because time has passed. If a terminal failure is reported, reconcile it before using a new key.
SIGNING_REJECTEDThe custody provider refused to sign. It applies its own policy before signing, independently of the platform.Check the custody provider's record of the request and its policy. Resubmitting unchanged is likely to be refused again. Escalate through the custody support route agreed for your deployment, with the operation and signing-request identifiers.
UNPROCESSABLESomething the operation depends on is missing, such as an active contract or account, or a lock expiry was in the past.Fix the dependency, then submit a new operation.
CHAIN_FAILEDThe ledger transaction reverted. The API Reference describes this as not worth retrying.Read reasonCode. Correct the cause (for example an amount above the balance), then submit a new operation.
RETRY_EXHAUSTEDThe platform retried a temporary failure of a system the operation depends on, and ran out of attempts.Reconcile the terminal operation and break evidence before a new business-authorized instruction. If a hash exists, verify the ledger outcome first; retry exhaustion alone does not prove that a broadcast transaction cannot settle.

Operations that stop moving

An operation that is not terminal may still be progressing. Do not submit a replacement while the original can still settle: both could confirm.

  • Detect it. Alert when an expected completion webhook does not arrive, or when an operation stays in a non-terminal state beyond its expected window. The window is agreed with Quant Network during onboarding.
  • Check the breaks queue. GET /api/transactions/breaks lists every operation that failed or stopped moving, newest first, until the case is resolved. reasonCode is an open set: mostly the failure codes above, plus RECON for an operation the ledger cannot account for.
  • A RECON row is not a failure yet. The operation was submitted and its ledger transaction has not been found. The ledger is the authority. If the transaction is included, the operation confirms. Ask the operator to establish whether the same prepared transaction can be re-driven safely under the deployed recovery controls. Do not submit a replacement while the case is open.

Investigate and resolve breaks through the operator process agreed for your deployment. The public API reads break evidence but has no endpoint to change its investigation status or resolve it.

Failures and your core banking postings

The platform does not post to your core banking system. Your integration makes both postings.

SituationWhat to do
MINT fails after you moved the customer's funds to your control accountHold the cash exception until the original token outcome is established. Reverse the posting or authorise a replacement only after reconciling the failure and any signing/ledger uncertainty.
BURN failsNo tokens were redeemed. Do not pay out the fiat. Pay out only when the BURN reaches CONFIRMED.
An operation confirmed in errorA confirmed operation cannot be rolled back. Submit a compensating operation, for example a BURN for a MINT issued in error, and make the matching posting.

See Issue and redeem.

Worked example

Read a failed operation:

GET /api/transactions/3f7c1a90-5d2e-4b08-9a41-6c2b8e5d7f13 HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer <access token>
API-Version: 1.0.0
HTTP/1.1 200 OK
Content-Type: application/json

{
  "transactionId": "3f7c1a90-5d2e-4b08-9a41-6c2b8e5d7f13",
  "txType": "MINT",
  "assetId": "GBP",
  "accountRef": "ACC-0001",
  "customerRef": "CUST-0001",
  "counterpartyRef": null,
  "amount": "10.50",
  "params": {},
  "state": "FAILED",
  "failureCode": "CHAIN_FAILED",
  "reasonCode": "EXECUTION_REVERTED",
  "txHash": "0x4a1b8c7d2e5f309a6b1c4d7e8f0a2b3c5d6e7f8091a2b3c4d5e6f7089a1b2c3d",
  "blockNumber": 18446801,
  "createdAt": "2026-08-06T09:29:10.285Z",
  "updatedAt": "2026-08-06T09:33:12.550Z"
}

txHash and blockNumber are both present, so the ledger transaction was included and reverted. No value moved.

List what stopped moving for the account:

GET /api/transactions/breaks?accountRef=ACC-0001 HTTP/1.1
Host: api.qfp.quantnetwork.net
Authorization: Bearer <access token>
API-Version: 1.0.0
HTTP/1.1 200 OK
Content-Type: application/json

{
  "content": [
    {
      "transactionId": "9c4d2f18-73ba-4e6c-8f05-2d17ab903e64",
      "reasonCode": "RECON",
      "occurredAt": "2026-09-18T09:02:00.000Z",
      "txType": "MINT",
      "state": "SUBMITTED",
      "accountRef": "ACC-0001",
      "assetId": "GBP",
      "txHash": "0x9f2c1d5e4b8a37c06d1e5f9a2b4c7d8e0f1a2b3c4d5e6f708192a3b4c5d6e7f8",
      "breakId": "5e1b7c92-0a34-4d18-b6f7-2c9e4a8d3b51",
      "breakStatus": "OPEN",
      "detail": {
        "reason": "no chain transaction found within the search window",
        "nonce": 41
      }
    },
    {
      "transactionId": "3f7c1a90-5d2e-4b08-9a41-6c2b8e5d7f13",
      "reasonCode": "CHAIN_FAILED",
      "occurredAt": "2026-09-18T09:01:00.000Z",
      "txType": "BURN",
      "state": "FAILED",
      "accountRef": "ACC-0001",
      "assetId": "GBP",
      "txHash": "0x4b81e0c7a2d93f16580ea7c4b39d2f85a61c07be3d49128f0a5c6b2e4d17f903",
      "breakId": "c73a2f45-8e1d-4906-a2b8-7f0c5d31e9a4",
      "breakStatus": "INVESTIGATING",
      "detail": {
        "failure": "CONTRACT_REVERTED",
        "reasonDetail": "execution reverted: balance below amount",
        "attempts": 1
      }
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 2,
  "totalPages": 1
}

The first row is a MINT that was submitted and not yet found on the ledger: wait, do not resubmit. The second is a BURN that reverted because the balance was below the amount: correct the amount and submit a new operation with a new key.

View in API Reference

Errors and edge cases

  • detail has no fixed schema. Its keys depend on the path that opened the case, and new keys can appear. Read it defensively.
  • Treasury occurredAt uses ISO 8601 UTC with three fractional digits, and can be sent back as a from filter. The from and to filters take ISO 8601. The window is half-open: from is inclusive, to exclusive.
  • An unrecognised reason filter returns an empty page, not a 400.
  • A 404 on a RELEASE or CANCEL naming a new lockId is worth retrying. The lock becomes visible shortly after its LOCK is submitted.
  • On a CANCELLED operation, failureCode may hold free text. Do not validate it against the enumeration.
  • Webhook event types for failures include transaction.failed, transaction.rejected and transaction.cancelled. Treat both failure and rejection notifications as prompts to read the operation. Use its actual state and failure evidence for business decisions, rather than inferring the outcome from the event name.

Follow a Recipe

Step through the code in JavaScript or Python, inspect illustrative responses, and open the connected endpoint schemas.

API Reference

Open an endpoint for request fields, response examples and the interactive API console.

Related pages


Did this page help you?