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 create a new movement from an existing template with fresh parameters (amount, memo, and destination data where the template permits them). Movement Authorization Model distinguishes that route definition from allowlist registration, approval, and dispatch authority.

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 permits (destination address, memo, network, etc.). Creation persists a request but does not submit to a provider.

2. Compile

QTG compiles the request into a DAG plan. This involves validating the graph structure, resolving bindings (which exchange credentials or signer metadata to use), and computing a tamper-evident hash of the compiled plan. Applicable on-chain intent addresses are checked at creation.
That timing is not universal. A CEX-only template does not run the CEX local-address or exchange address-book check at creation. Its QTG local allowlist check first runs in dispatch after approval, and an exchange address-book pre-read occurs only where the adapter supports it. A created CEX movement is not evidence that its destination is approved. See Allowlist Operator QA.

3. Approve (the gate)

The movement begins in PENDING_APPROVAL. Manual approval locks the movement and requires the client to echo its compiled-plan hash. When an existing eligible auto-approve hook succeeds during creation, QTG applies the same approval/reservation path without waiting for a manual action; agent-originated movements still park for human approval.
Auto-approve policies are scoped by template and strategy. They do not bypass approval or dispatch guards; they evaluate whether QTG may apply approval automatically. The policy trace is evidence, not final authority by itself. If no hook succeeds, the movement waits for manual approval until it expires or is rejected.

4. Dispatch

Once approved, the dispatcher picks up each ready node in the plan and hands it to the appropriate executor. Before that executor can perform a side effect, dispatch recomputes the compiled-plan hash and repeats the applicable QTG address check, including cooling-period enforcement. For CEX withdrawals, this is the first QTG local destination check; the executor then performs the template literal-pin check and, where available, an exchange saved-address-book pre-read. For an on-chain bridge, the executor builds and signs a transaction through the configured signer path only after its dispatch guards pass. See Movement Authorization Model for the difference between registration, approval, and this revalidation boundary.

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 the required terminal states, the movement is marked complete. A definitive failure can mark it failed; an ambiguous side effect can instead require manual intervention while recovery reconciles provider evidence. State transitions are recorded, and configured callback events are delivered through the callback outbox.

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 asks QTG to create a movement.
  2. QTG compiles the plan and places it in PENDING_APPROVAL unless an existing eligible auto-approve hook applies.
  3. If the request has a matching callback configuration, the callback outbox can notify the receiver that approval is needed.
  4. The Dashboard approval dialog requests a DB-only, digest-bound Approval Brief showing source, destination, asset, amount semantics, guard verdicts, and the compiled-plan hash plus brief/authorization-facts digests; you submit the captured digest (enforced under MG_AUTHORIZATION_PROJECTION_MODE=enforce, shadow by default).
  5. A manual approval validates the echoed hash and then makes the movement runnable. Dispatch still performs its own hash, allowlist, and executor/provider checks before any transfer is submitted.
If the bot had a bug and tried to move your entire balance, the movement normally remains pending until approval and reservation controls allow it. The approval gate is one safety layer; it is not a substitute for the allowlist, caps, or dispatch revalidation described in the Movement Authorization Model.

What Happens on Failure

Not every transfer succeeds on the first try. QTG handles failures at three levels: The request state follows the node outcomes: a definitive failure can end in FAILED, while ambiguous side effects can require MANUAL_INTERVENTION. Relevant transitions use the configured callback outbox; QTG does not treat ambiguity as permission to retry a submission indefinitely.
For the full state machine specification with all 12 node states and transition rules, see States & Transitions.

Callbacks: Keeping External Systems in Sync

For a movement with matching callback configuration and event filters, externally relevant state changes use a callback outbox. 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.