> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quantumapi.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Transaction lifecycle

> Understand the 14-state transaction machine, from submission through screening, signing, broadcast, and confirmation.

## 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

```mermaid theme={null}
stateDiagram-v2
    [*] --> SUBMITTED: POST /v1/transactions
    SUBMITTED --> PENDING_AUTHORIZATION: Policy requires approval
    SUBMITTED --> PENDING_AML_SCREENING: Screening required
    SUBMITTED --> PENDING_SIGNATURE: Auto-approved
    SUBMITTED --> REJECTED: Policy denied

    PENDING_AUTHORIZATION --> APPROVED: POST /approve
    PENDING_AUTHORIZATION --> REJECTED: POST /reject
    PENDING_AUTHORIZATION --> CANCELLED: POST /cancel

    APPROVED --> PENDING_SIGNATURE: Authorization complete

    PENDING_AML_SCREENING --> PENDING_SIGNATURE: Cleared
    PENDING_AML_SCREENING --> BLOCKED: Flagged / blocked

    PENDING_SIGNATURE --> SIGNED: Signature submitted
    PENDING_SIGNATURE --> FAILED: Invalid signature
    PENDING_SIGNATURE --> CANCELLED: POST /cancel

    SIGNED --> BROADCASTING: Assembling tx
    BROADCASTING --> CONFIRMING: Broadcast success
    BROADCASTING --> FAILED: Broadcast rejected

    CONFIRMING --> COMPLETED: Confirmation depth reached
    CONFIRMING --> FAILED: Reverted/dropped

    COMPLETED --> [*]
    FAILED --> [*]
    REJECTED --> [*]
    CANCELLED --> [*]
    BLOCKED --> [*]
```

## States explained

### Active states

<AccordionGroup>
  <Accordion title="SUBMITTED" icon="circle-plus">
    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`
  </Accordion>

  <Accordion title="QUEUED" icon="hourglass-half">
    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.
  </Accordion>

  <Accordion title="PENDING_AUTHORIZATION" icon="clock">
    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`
  </Accordion>

  <Accordion title="PENDING_AML_SCREENING" icon="shield-halved">
    Compliance screening was submitted to the AML provider. The transaction is paused until the provider returns a result.

    **Webhook event:** `screening.submitted`
  </Accordion>

  <Accordion title="APPROVED" icon="circle-check">
    An approver has approved the transaction. It immediately transitions to `PENDING_SIGNATURE`.

    **Webhook event:** `approval.decision`
  </Accordion>

  <Accordion title="PENDING_SIGNATURE" icon="pen-nib">
    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_changed`

    This is the **key integration point**. See the [External signing guide](/guides/external-signing).
  </Accordion>

  <Accordion title="SIGNED" icon="signature">
    The signature has been validated against the registered wallet's public key. The service is assembling the raw signed transaction.
  </Accordion>

  <Accordion title="BROADCASTING" icon="tower-broadcast">
    The signed transaction is being sent to the Quantum Chain network via the node pool.
  </Accordion>

  <Accordion title="CONFIRMING" icon="spinner">
    The transaction has been broadcast and is awaiting on-chain confirmation. The service polls for the transaction receipt and tracks the confirmation depth.
  </Accordion>
</AccordionGroup>

### Terminal states

<AccordionGroup>
  <Accordion title="COMPLETED" icon="circle-check">
    The transaction has reached the required confirmation depth. The `chainTxHash`, `blockNumber`, and `confirmedAt` fields are populated.

    **Webhook event:** `transaction.completed`
  </Accordion>

  <Accordion title="REJECTED" icon="ban">
    A policy denied the transaction (e.g., amount exceeds limit, destination blacklisted) or an approver explicitly rejected it. Check `failureReason` for details.
  </Accordion>

  <Accordion title="FAILED" icon="triangle-exclamation">
    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`
  </Accordion>

  <Accordion title="CANCELLED" icon="xmark">
    The transaction was cancelled by the integrator before signing and broadcast. Cancel is only valid from `SUBMITTED`, `PENDING_AUTHORIZATION`, `APPROVED`, or `PENDING_SIGNATURE`.
  </Accordion>

  <Accordion title="BLOCKED" icon="shield-xmark">
    Compliance screening flagged or blocked the transaction. The transaction never reaches the chain.

    **Webhook event:** `screening.blocked`
  </Accordion>
</AccordionGroup>

## Happy path timeline

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

| Step | State               | Who                  | Duration                   |
| ---- | ------------------- | -------------------- | -------------------------- |
| 1    | `SUBMITTED`         | API caller           | Instant                    |
| 2    | `PENDING_SIGNATURE` | Auto (policy engine) | \< 100ms                   |
| 3    | `SIGNED`            | External signer      | Depends on your infra      |
| 4    | `BROADCASTING`      | Qustody service      | \< 1s                      |
| 5    | `CONFIRMING`        | Qustody service      | Network block time         |
| 6    | `COMPLETED`         | Qustody service      | Confirmation depth reached |

## Polling vs. webhooks

You can track transaction status two ways:

<CardGroup cols={2}>
  <Card title="Polling" icon="arrows-rotate">
    Call `GET /v1/transactions/{id}` periodically. Simple but adds latency and load.

    ```bash theme={null}
    curl https://api.quantumchain.io/v1/transactions/{id} \
      -H "Authorization: Bearer $API_KEY"
    ```
  </Card>

  <Card title="Webhooks (recommended)" icon="bell">
    Register a webhook endpoint and receive real-time events at every state transition.

    See the [Webhooks guide](/concepts/webhooks).
  </Card>
</CardGroup>

## Filtering transactions

List transactions with filters for monitoring:

```bash theme={null}
# All confirming transactions
curl "https://api.quantumchain.io/v1/transactions?status=CONFIRMING" \
  -H "Authorization: Bearer $API_KEY"

# All transactions for a specific vault
curl "https://api.quantumchain.io/v1/transactions?vaultId=vault-uuid" \
  -H "Authorization: Bearer $API_KEY"

# All transactions for a specific wallet and asset
curl "https://api.quantumchain.io/v1/transactions?walletId=wallet-uuid&assetId=asset-uuid" \
  -H "Authorization: Bearer $API_KEY"
```

## Error handling

<Warning>
  Always check `failureReason` on terminal states (`FAILED`, `REJECTED`,
  `CANCELLED`, `BLOCKED`). It contains a human-readable explanation of what
  went wrong.
</Warning>

Common failure reasons:

| Reason                          | State    | Resolution                                                                |
| ------------------------------- | -------- | ------------------------------------------------------------------------- |
| `amount exceeds max`            | REJECTED | Lower the amount or adjust the `MAX_AMOUNT` policy                        |
| `destination is blacklisted`    | REJECTED | Remove the address from the `BLACKLIST_ADDRESS` policy                    |
| `screening blocked`             | BLOCKED  | Review the screening report; remediate or contact compliance              |
| `nonce too low`                 | FAILED   | The nonce was already used. Retry creates a new nonce                     |
| `insufficient funds`            | FAILED   | Fund the source wallet with enough QUANTUM for amount + gas               |
| `signature verification failed` | FAILED   | Verify you're signing the correct payload with the correct registered key |
