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

# Movement Lifecycle

> How a transfer request flows from creation to completion

# Movement Lifecycle

Every transfer in QTG follows the same lifecycle, whether you are moving 10 USDC between test wallets or routing a six-figure rebalance across three exchanges. Understanding this lifecycle is the single most important concept for operating QTG.

## What is a Movement?

A **Movement** is a transfer request that has been compiled into a plan. You start by choosing a **template** -- a reusable blueprint that describes *which* venue to use, *what* asset to move, and *where* it goes. When you create a movement from that template, QTG compiles it into a **plan**: a directed acyclic graph (DAG) of steps that must execute in order.

For example, the template "Move USDC from Upbit to Binance" compiles into three steps: withdraw from Upbit, watch the withdrawal complete, then watch the deposit arrive at Binance. Each step is called a **node**, and each node runs through a venue-specific executor.

<Tip>
  Templates are registered once and reused. You don't re-describe the transfer shape every time -- you just create a new movement from an existing template with fresh parameters (amount, memo, etc.).
</Tip>

## The Six Stages

Every movement passes through these stages:

```mermaid theme={null}
flowchart LR
    A["Create"] --> B["Compile"]
    B --> C["Approve"]
    C --> D["Dispatch"]
    D --> E["Observe"]
    E --> F["Complete"]

    style C fill:#f59e0b,stroke:#d97706,color:#000
```

### 1. Create

You submit a transfer request via the API, CLI, or MCP tool. You specify the template, the amount, and any parameters the template requires (destination address, memo, network, etc.).

### 2. Compile

QTG compiles the request into a DAG plan. This involves validating the graph structure, resolving bindings (which exchange credentials to use, which signer to use), and computing a tamper-evident hash of the entire plan. If the template references an address not on the allowlist, compilation fails here -- before anything moves.

### 3. Approve (the gate)

The movement enters **PENDING\_APPROVAL** and stops. Nothing executes until an operator (or an auto-approve policy) explicitly approves it. This is the core safety mechanism: even if an agent or automated system creates hundreds of movements, none of them move funds until a human -- or a policy the human configured -- says yes.

<Warning>
  Auto-approve policies are available but scoped by template and strategy. They do not bypass the approval stage; they evaluate it automatically. If no policy matches, the movement waits for manual approval indefinitely.
</Warning>

### 4. Dispatch

Once approved, the dispatcher picks up each ready node in the plan and hands it to the appropriate executor. For a CEX withdrawal, the executor calls the exchange API. For an on-chain bridge, the executor builds and signs a transaction via your KMS. Each node goes through a preflight check before execution -- including a re-check of the destination address against the allowlist.

### 5. Observe

After dispatch, the observer polls for completion. For CEX transfers this means checking withdrawal and deposit status via the exchange API. For on-chain transfers this means watching for transaction confirmation. The observer handles the inherent uncertainty of external systems: API timeouts, temporary failures, and ambiguous states.

### 6. Complete

When all nodes in the DAG have reached a terminal state, the movement is marked complete (or failed, if a node could not recover). The final state is recorded, audit events are written, and callbacks fire.

## The Approval Gate in Practice

The approval gate is what makes QTG different from a plain API wrapper. Consider this scenario:

1. Your trading bot detects an arbitrage opportunity and signals QTG to move 50,000 USDC from Upbit to Binance.
2. QTG compiles the plan and places it in PENDING\_APPROVAL.
3. You get a callback notification on your Slack webhook.
4. You review the amount, destination, and route in the dashboard.
5. You approve. The transfer executes.

If the bot had a bug and tried to move your entire balance, the movement would still be sitting at PENDING\_APPROVAL waiting for your approval. The gate is the safety net.

## What Happens on Failure

Not every transfer succeeds on the first try. QTG handles failures at three levels:

| Situation                                                                                     | What QTG does                                                                                                                |
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Temporary failure** (API timeout, rate limit)                                               | The observer retries with backoff. No operator action needed.                                                                |
| **Ambiguous state** (exchange returned an error but the withdrawal might have been submitted) | The node enters UNKNOWN state. The recovery worker attempts to reconcile by checking the exchange for matching transactions. |
| **Fatal failure** (invalid address, insufficient balance, exchange rejected)                  | The node is marked FAILED. The movement may be retried with corrected parameters, or cancelled.                              |

If a node fails and cannot recover, QTG pauses the movement and fires a callback so you know immediately. It does not silently retry indefinitely or leave funds in limbo.

<Note>
  For the full state machine specification with all 12 node states and transition rules, see [States & Transitions](/reference/domain/states-and-transitions).
</Note>

## Callbacks: Keeping External Systems in Sync

Every meaningful state change fires a **callback** to any URL you registered when creating the movement. Callbacks are:

* **Durable** -- stored in an outbox table and retried on delivery failure.
* **Signed** -- HMAC v3 with a nonce, so receivers can verify authenticity and reject replays.
* **Non-blocking** -- callback delivery does not delay the movement itself.

This is how your dashboard, Slack bot, or trading controller stays informed without polling.

For callback receiver implementation details, see [Callback Verification Contract](/callback-verification-contract).
