Skip to main content

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.
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.).

The Six Stages

Every movement passes through these stages:

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

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: 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.
For the full state machine specification with all 12 node states and transition rules, see States & Transitions.

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.