Skip to main content

Overview

Every transfer in Qustody follows a deterministic state machine of 14 states. Understanding these states is critical for reliable integrations. Your system should handle each transition and respond to webhook events accordingly.

State machine diagram

States explained

Active states

Transaction has been accepted, validated, gas estimated, and a nonce assigned. Policy and screening evaluation happens immediately. The transaction transitions to PENDING_SIGNATURE, PENDING_AUTHORIZATION, PENDING_AML_SCREENING, or REJECTED.Webhook event: transaction.created
Held briefly because of an idempotency conflict (a duplicate Idempotency-Key) or a tenant rate limit. The transaction returns to SUBMITTED once a slot is available.
A policy rule of type REQUIRE_APPROVAL matched. The transaction stays here until an authorized user calls POST /v1/transactions/{id}/approve or POST /v1/transactions/{id}/reject.Webhook event: approval.required
Compliance screening was submitted to the AML provider. The transaction is paused until the provider returns a result.Webhook event: screening.submitted
An approver has approved the transaction. It immediately transitions to PENDING_SIGNATURE.Webhook event: approval.decision
The transaction is ready for external signing. Call GET /v1/transactions/{id}/signing_payload to retrieve the digest, sign it with your post-quantum private key, and submit via POST /v1/transactions/{id}/signature.Webhook event: transaction.status_changedThis is the key integration point. See the External signing guide.
The signature has been validated against the registered wallet’s public key. The service is assembling the raw signed transaction.
The signed transaction is being sent to the Quantum Chain network via the node pool.
The transaction has been broadcast and is awaiting on-chain confirmation. The service polls for the transaction receipt and tracks the confirmation depth.

Terminal states

The transaction has reached the required confirmation depth. The chainTxHash, blockNumber, and confirmedAt fields are populated.Webhook event: transaction.completed
A policy denied the transaction (e.g., amount exceeds limit, destination blacklisted) or an approver explicitly rejected it. Check failureReason for details.
The transaction failed during signing (invalid signature), broadcast (node rejected), or confirmation (reverted on-chain or dropped from mempool). Check failureReason.Webhook event: transaction.failed
The transaction was cancelled by the integrator before signing and broadcast. Cancel is only valid from SUBMITTED, PENDING_AUTHORIZATION, APPROVED, or PENDING_SIGNATURE.
Compliance screening flagged or blocked the transaction. The transaction never reaches the chain.Webhook event: screening.blocked

Happy path timeline

Here’s what a typical successful transfer looks like (auto-approved, no screening hold):

Polling vs. webhooks

You can track transaction status two ways:

Polling

Call GET /v1/transactions/{id} periodically. Simple but adds latency and load.

Webhooks (recommended)

Register a webhook endpoint and receive real-time events at every state transition.See the Webhooks guide.

Filtering transactions

List transactions with filters for monitoring:

Error handling

Always check failureReason on terminal states (FAILED, REJECTED, CANCELLED, BLOCKED). It contains a human-readable explanation of what went wrong.
Common failure reasons: