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

> Formal definition of the 14 transaction states, valid transitions, and trigger conditions.

Every transaction submitted to Qustody flows through a deterministic state machine with **14 states**. The machine is enforced server-side: only legal transitions are accepted, and every transition emits a webhook event you can subscribe to.

## State diagram

```mermaid theme={null}
stateDiagram-v2
    direction TB

    [*] --> SUBMITTED: POST /v1/transactions

    SUBMITTED --> QUEUED: Idempotency / rate limit
    QUEUED --> SUBMITTED: Slot available

    SUBMITTED --> REJECTED: Policy denies
    SUBMITTED --> PENDING_AUTHORIZATION: Policy requires approval
    SUBMITTED --> PENDING_AML_SCREENING: AML screening required
    SUBMITTED --> PENDING_SIGNATURE: Auto-approved

    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: Screening cleared
    PENDING_AML_SCREENING --> BLOCKED: Screening flagged / blocked

    PENDING_SIGNATURE --> SIGNED: POST /signature (valid)
    PENDING_SIGNATURE --> FAILED: Invalid signature
    PENDING_SIGNATURE --> CANCELLED: POST /cancel

    SIGNED --> BROADCASTING: Assembled raw tx
    SIGNED --> FAILED: Assembly failed

    BROADCASTING --> CONFIRMING: Node accepted
    BROADCASTING --> FAILED: Node rejected

    CONFIRMING --> COMPLETED: Confirmation depth reached
    CONFIRMING --> FAILED: Revert / dropped from mempool

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

## All 14 states

<CardGroup cols={2}>
  <Card title="SUBMITTED">Accepted by the API; awaiting policy and screening evaluation.</Card>
  <Card title="QUEUED">Held briefly because of an idempotency conflict or rate limit.</Card>
  <Card title="PENDING_AUTHORIZATION">A policy rule of type `REQUIRE_APPROVAL` matched. A human approver must decide.</Card>
  <Card title="PENDING_AML_SCREENING">Compliance screening is in flight; the transaction is paused.</Card>
  <Card title="APPROVED">Authorization granted; about to enter signing.</Card>
  <Card title="PENDING_SIGNATURE">Awaiting an external post-quantum signature over the signing payload.</Card>
  <Card title="SIGNED">Signature received and verified against the registered wallet's public key.</Card>
  <Card title="BROADCASTING">Raw transaction assembled and being broadcast to Quantum Chain nodes.</Card>
  <Card title="CONFIRMING">Included on-chain; counting confirmations to the configured depth.</Card>
  <Card title="COMPLETED">Confirmed with sufficient depth. Terminal.</Card>
  <Card title="FAILED">Permanent failure during signing, broadcast, or confirmation. Terminal.</Card>
  <Card title="REJECTED">Denied by the policy engine or by an approver. Terminal.</Card>
  <Card title="CANCELLED">Cancelled by the caller before broadcast. Terminal.</Card>
  <Card title="BLOCKED">Compliance screening flagged or blocked the transaction. Terminal.</Card>
</CardGroup>

### Active vs terminal

* **Active (9):** `SUBMITTED`, `QUEUED`, `PENDING_AUTHORIZATION`, `PENDING_AML_SCREENING`, `APPROVED`, `PENDING_SIGNATURE`, `SIGNED`, `BROADCASTING`, `CONFIRMING`.
* **Terminal (5):** `COMPLETED`, `FAILED`, `REJECTED`, `CANCELLED`, `BLOCKED`. Once a transaction reaches a terminal state it never changes again.

## Transition table

| From                    | To                      | Trigger                                                                  | Actor           |
| ----------------------- | ----------------------- | ------------------------------------------------------------------------ | --------------- |
| —                       | `SUBMITTED`             | `POST /v1/transactions`                                                  | API caller      |
| `SUBMITTED`             | `QUEUED`                | Idempotency conflict or rate limit                                       | System          |
| `QUEUED`                | `SUBMITTED`             | Slot available                                                           | System          |
| `SUBMITTED`             | `REJECTED`              | Policy engine denies (e.g. `BLACKLIST_ADDRESS`, `MAX_AMOUNT`)            | System          |
| `SUBMITTED`             | `PENDING_AUTHORIZATION` | Policy rule of type `REQUIRE_APPROVAL` matched                           | System          |
| `SUBMITTED`             | `PENDING_AML_SCREENING` | AML provider screening required                                          | System          |
| `SUBMITTED`             | `PENDING_SIGNATURE`     | Policy auto-approved and no screening required                           | System          |
| `PENDING_AUTHORIZATION` | `APPROVED`              | `POST /v1/transactions/{id}/approve`                                     | Approver        |
| `PENDING_AUTHORIZATION` | `REJECTED`              | `POST /v1/transactions/{id}/reject`                                      | Approver        |
| `PENDING_AUTHORIZATION` | `CANCELLED`             | `POST /v1/transactions/{id}/cancel`                                      | API caller      |
| `APPROVED`              | `PENDING_SIGNATURE`     | Automatic after authorization                                            | System          |
| `APPROVED`              | `CANCELLED`             | `POST /v1/transactions/{id}/cancel`                                      | API caller      |
| `PENDING_AML_SCREENING` | `PENDING_SIGNATURE`     | Screening cleared                                                        | System          |
| `PENDING_AML_SCREENING` | `BLOCKED`               | Screening flagged or blocked                                             | System          |
| `PENDING_SIGNATURE`     | `SIGNED`                | `POST /v1/transactions/{id}/signature` with valid post-quantum signature | External signer |
| `PENDING_SIGNATURE`     | `FAILED`                | Signature verification failed                                            | System          |
| `PENDING_SIGNATURE`     | `CANCELLED`             | `POST /v1/transactions/{id}/cancel`                                      | API caller      |
| `SIGNED`                | `BROADCASTING`          | Raw transaction assembled                                                | System          |
| `SIGNED`                | `FAILED`                | Assembly error                                                           | System          |
| `BROADCASTING`          | `CONFIRMING`            | Node accepted the transaction                                            | System          |
| `BROADCASTING`          | `FAILED`                | Node rejected (bad nonce, insufficient gas, etc.)                        | System          |
| `CONFIRMING`            | `COMPLETED`             | Confirmation depth reached                                               | System          |
| `CONFIRMING`            | `FAILED`                | Reverted or dropped from mempool                                         | System          |

<Note>
  `POST /v1/transactions/{id}/cancel` is only valid from `SUBMITTED`,
  `PENDING_AUTHORIZATION`, `APPROVED`, or `PENDING_SIGNATURE`. Once a transaction
  is `SIGNED`, `BROADCASTING`, or `CONFIRMING`, it cannot be cancelled.
</Note>

## Webhook events per state

| State entered           | Webhook event                                                         |
| ----------------------- | --------------------------------------------------------------------- |
| `SUBMITTED`             | `transaction.created`                                                 |
| `QUEUED`                | `transaction.status_changed`                                          |
| `PENDING_AUTHORIZATION` | `approval.required` + `transaction.status_changed`                    |
| `PENDING_AML_SCREENING` | `screening.submitted` + `transaction.status_changed`                  |
| `APPROVED`              | `approval.decision` + `transaction.status_changed`                    |
| `REJECTED`              | `approval.decision` (if from approver) + `transaction.status_changed` |
| `PENDING_SIGNATURE`     | `transaction.status_changed`                                          |
| `SIGNED`                | `transaction.status_changed`                                          |
| `BROADCASTING`          | `transaction.status_changed`                                          |
| `CONFIRMING`            | `transaction.status_changed`                                          |
| `COMPLETED`             | `transaction.completed`                                               |
| `FAILED`                | `transaction.failed`                                                  |
| `CANCELLED`             | `transaction.status_changed`                                          |
| `BLOCKED`               | `screening.blocked` + `transaction.status_changed`                    |

The full list of 12 webhook event types is documented in [Webhooks](/concepts/webhooks).

## Integration pattern

```mermaid theme={null}
sequenceDiagram
    participant You
    participant API as Qustody API
    participant Signer as External signer
    participant WH as Your webhook

    You->>API: POST /v1/transactions
    API-->>You: 201 {status: SUBMITTED, id}
    API-->>WH: transaction.created

    Note over API: Policy + screening evaluation
    API-->>WH: transaction.status_changed → PENDING_SIGNATURE

    You->>API: GET /v1/transactions/{id}/signing_payload
    API-->>You: {signing_payload: "0x..."}

    You->>Signer: Sign payload with post-quantum key
    Signer-->>You: post-quantum signature + public key

    You->>API: POST /v1/transactions/{id}/signature
    API-->>You: 200 {status: SIGNED}
    API-->>WH: transaction.status_changed → BROADCASTING
    API-->>WH: transaction.status_changed → CONFIRMING

    Note over API: Waiting for confirmation depth...

    API-->>WH: transaction.completed
```

<Tip>
  Use `transaction.status_changed` to track all intermediate transitions,
  `transaction.completed` / `transaction.failed` for final outcomes,
  `approval.required` to drive approver UIs, and `screening.*` to surface
  compliance state in your dashboards.
</Tip>
