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

# State Machine

> Complete reference for the @bluvo/sdk-ts state machine, all 35 flow states, hook booleans, context data, and callable actions

## Overview

The `useBluvoFlow` hook from `@bluvo/react` is powered by a finite state machine that orchestrates the entire withdrawal lifecycle, from exchange selection through OAuth, wallet loading, quoting, and withdrawal execution (including 2FA). Every state transition is deterministic: given a state and an action, the next state is always predictable.

Your UI renders based on the current state. The hook exposes each state as a boolean property (e.g., `flow.isOAuthPending`, `flow.requires2FA`), so you never need to inspect raw state strings directly.

<Tip>
  If you're implementing OAuth for the first time, start with [OAuth2 Integration](/learn/oauth2-integration) for a step-by-step walkthrough. This page is a reference for when you need to look up specific states, booleans, or transitions.
</Tip>

***

## Flow Diagram

### Happy Path (OAuth)

```mermaid theme={null}
%%{init: {'flowchart': {'defaultRenderer': 'elk'}}}%%
flowchart TD
    idle["idle"]
    exc_loading["exchanges:loading"]
    exc_ready["exchanges:ready"]
    oauth_waiting["oauth:waiting"]
    oauth_processing["oauth:processing"]
    oauth_completed["oauth:completed"]
    oauth_error["oauth:error"]:::error
    oauth_fatal["oauth:fatal"]:::error
    oauth_closed["oauth:window_closed_by_user"]:::error
    wallet_loading["wallet:loading"]
    wallet_ready["wallet:ready"]
    quote_requesting["quote:requesting"]
    quote_ready["quote:ready"]
    withdraw_processing["withdraw:processing"]
    withdraw_completed["withdraw:completed"]:::success

    idle --> exc_loading
    exc_loading --> exc_ready
    exc_ready --> oauth_waiting
    idle --> oauth_waiting
    oauth_waiting --> oauth_processing
    oauth_processing --> oauth_completed
    oauth_processing --> oauth_error
    oauth_processing --> oauth_fatal
    oauth_processing --> oauth_closed
    oauth_completed --> wallet_loading
    wallet_loading --> wallet_ready
    wallet_ready --> quote_requesting
    quote_requesting --> quote_ready
    quote_ready --> withdraw_processing
    withdraw_processing --> withdraw_completed

    classDef error fill:#fca5a5,stroke:#ef4444,color:#7f1d1d
    classDef success fill:#86efac,stroke:#22c55e,color:#14532d
```

### Happy Path (QR Code Login)

```mermaid theme={null}
flowchart LR
    idle["idle"]
    qr_waiting["qrcode:waiting"]
    qr_displaying["qrcode:displaying"]
    qr_scanning["qrcode:scanning"]
    oauth_completed["oauth:completed"]
    wallet_loading["wallet:loading"]
    more["..."]
    qr_timeout["qrcode:timeout"]:::error
    qr_error["qrcode:error"]:::error
    qr_fatal["qrcode:fatal"]:::error

    idle --> qr_waiting
    qr_waiting --> qr_displaying
    qr_displaying --> qr_scanning
    qr_scanning --> oauth_completed
    oauth_completed --> wallet_loading
    wallet_loading --> more
    qr_displaying --> qr_timeout
    qr_timeout -- "refreshQRCode" --> qr_waiting
    qr_displaying --> qr_error
    qr_error -- "refreshQRCode" --> qr_waiting
    qr_displaying --> qr_fatal

    classDef error fill:#fca5a5,stroke:#ef4444,color:#7f1d1d
```

### Withdrawal Sub-Flow

```mermaid theme={null}
%%{init: {'flowchart': {'defaultRenderer': 'elk'}}}%%
flowchart TD
    quote_ready["quote:ready"]
    processing["withdraw:processing"]
    completed["withdraw:completed"]:::success
    error2fa["withdraw:error2FA"]
    error2fa_multi["withdraw:error2FAMultiStep"]
    ready_confirm["withdraw:readyToConfirm"]
    error_sms["withdraw:errorSMS"]
    error_kyc["withdraw:errorKYC"]:::error
    error_balance["withdraw:errorBalance"]:::error
    retrying["withdraw:retrying"]
    blocked["withdraw:blocked"]:::error
    fatal["withdraw:fatal"]:::error

    quote_ready --> processing
    processing --> completed
    processing --> error2fa
    error2fa -- "submit2FA" --> processing
    processing --> error2fa_multi
    error2fa_multi -- "submit2FAMultiStep" --> processing
    error2fa_multi --> ready_confirm
    ready_confirm -- "confirmWithdrawal" --> processing
    processing --> error_sms
    error_sms -- "submitSMS" --> processing
    processing --> error_kyc
    processing --> error_balance
    processing --> retrying
    retrying -- "auto" --> processing
    processing --> blocked
    processing --> fatal

    classDef error fill:#fca5a5,stroke:#ef4444,color:#7f1d1d
    classDef success fill:#86efac,stroke:#22c55e,color:#14532d
```

***

## States by Phase

### Idle

| State  | Hook Boolean | Description                      | Transitions Out                                        |
| ------ | ------------ | -------------------------------- | ------------------------------------------------------ |
| `idle` | `isIdle`     | Flow not started. Initial state. | `exchanges:loading`, `oauth:waiting`, `qrcode:waiting` |

***

### Exchanges

| State               | Hook Boolean         | Description                              | Transitions Out                      |
| ------------------- | -------------------- | ---------------------------------------- | ------------------------------------ |
| `exchanges:loading` | `isExchangesLoading` | Fetching list of supported exchanges     | `exchanges:ready`, `exchanges:error` |
| `exchanges:ready`   | `isExchangesReady`   | Exchange list loaded. User can pick one. | `oauth:waiting`, `qrcode:waiting`    |
| `exchanges:error`   | `isExchangesError`   | Failed to load exchanges                 | Terminal, call `cancel()` and retry  |

<Tip>
  `listExchanges()` manages its own loading state independently of the flow machine. You can call it before starting a flow, `isExchangesReady` becomes `true` when either the machine or the standalone call has data.
</Tip>

***

### OAuth

| State                         | Hook Boolean                       | Description                                         | Transitions Out                                                                |
| ----------------------------- | ---------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------ |
| `oauth:waiting`               | `isOAuthWaiting`                   | Popup opened, waiting for user to authenticate      | `oauth:processing`                                                             |
| `oauth:processing`            | `isOAuthProcessing`                | Token exchange in progress                          | `oauth:completed`, `oauth:error`, `oauth:fatal`, `oauth:window_closed_by_user` |
| `oauth:completed`             | `isOAuthComplete`                  | OAuth succeeded, auto-transitions to wallet loading | `wallet:loading`                                                               |
| `oauth:error`                 | `isOAuthError`                     | Recoverable error (network, timeout)                | Retry via `startWithdrawalFlow()` or `cancel()`                                |
| `oauth:fatal`                 | `isOAuthFatal`                     | Non-recoverable (exchange rejected connection)      | `cancel()` only                                                                |
| `oauth:window_closed_by_user` | `isOAuthWindowBeenClosedByTheUser` | User closed the popup manually                      | Retry via `startWithdrawalFlow()` or `cancel()`                                |

**Compound booleans:**

* `isOAuthPending` = `oauth:waiting` OR `oauth:processing`
* `isOAuthError` = `oauth:error` OR `oauth:fatal`
* `isWalletConnectionInvalid` = `oauth:fatal` OR `qrcode:fatal`

***

### QR Code

Used for exchanges that support QR-based login (e.g., Binance mobile app scanning).

| State               | Hook Boolean         | Description                          | Transitions Out                                                                        |
| ------------------- | -------------------- | ------------------------------------ | -------------------------------------------------------------------------------------- |
| `qrcode:waiting`    | `isQRCodeWaiting`    | Requesting QR code URL from exchange | `qrcode:displaying`, `qrcode:error`, `qrcode:fatal`                                    |
| `qrcode:displaying` | `isQRCodeDisplaying` | QR code visible, waiting for scan    | `qrcode:scanning`, `oauth:completed`, `qrcode:timeout`, `qrcode:error`, `qrcode:fatal` |
| `qrcode:scanning`   | `isQRCodeScanning`   | QR code scanned, completing auth     | `oauth:completed`, `qrcode:error`, `qrcode:fatal`                                      |
| `qrcode:error`      | `isQRCodeError`      | Recoverable QR code error            | `qrcode:waiting` (via `refreshQRCode()`)                                               |
| `qrcode:timeout`    | `isQRCodeTimeout`    | QR code expired                      | `qrcode:waiting` (via `refreshQRCode()`)                                               |
| `qrcode:fatal`      | `isQRCodeFatal`      | Non-recoverable QR code error        | `cancel()` only                                                                        |

**Compound booleans:**

* `isQRCodePending` = any `qrcode:*` state except error, fatal, timeout
* `isQRCodeError` = `qrcode:error` OR `qrcode:fatal`

**Context data available:**

* `qrCodeUrl`, the URL to render as a QR code
* `qrCodeExpiresAt`, Unix timestamp (ms) when the QR code expires
* `qrCodeStatus`, current status string from the exchange
* `isQRCodeFlow`, `true` when using QR code auth instead of OAuth popup

<Tip>
  After a successful QR code scan, the flow transitions to `oauth:completed` and reuses the same wallet-loading path as OAuth. From `wallet:loading` onward, the flow is identical regardless of authentication method.
</Tip>

***

### Wallet

| State            | Hook Boolean      | Description                               | Transitions Out                     |
| ---------------- | ----------------- | ----------------------------------------- | ----------------------------------- |
| `wallet:loading` | `isWalletLoading` | Fetching wallet balances after connection | `wallet:ready`, `wallet:error`      |
| `wallet:ready`   | `isWalletReady`   | Balances loaded, ready for quoting        | `quote:requesting`                  |
| `wallet:error`   | `isWalletError`   | Failed to load wallet                     | Terminal, call `cancel()` and retry |

**Error detection helpers:**

* `hasWalletNotFoundError`, wallet ID no longer valid (deleted or expired)
* `hasInvalidCredentialsError`, exchange tokens revoked; user must re-authenticate

***

### Quote

| State              | Hook Boolean     | Description                               | Transitions Out                                                      |
| ------------------ | ---------------- | ----------------------------------------- | -------------------------------------------------------------------- |
| `quote:requesting` | `isQuoteLoading` | Requesting withdrawal quote from exchange | `quote:ready`, `quote:error`                                         |
| `quote:ready`      | `isQuoteReady`   | Quote received with fee breakdown         | `withdraw:processing`, `quote:requesting` (refresh), `quote:expired` |
| `quote:expired`    | `isQuoteExpired` | Quote TTL elapsed                         | `quote:requesting` (via `requestQuote()`)                            |
| `quote:error`      | `isQuoteError`   | Quote request failed                      | Retry via `requestQuote()`                                           |

**Error detection helpers:**

* `hasAmountError`, amount below minimum or above maximum
* `hasAddressError`, invalid destination address format
* `hasNetworkError`, unsupported or invalid network

<Tip>
  Quotes have a TTL (check `quote.expiresAt`). If `autoRefreshQuotation` is enabled in options, the hook automatically requests a fresh quote before expiry.
</Tip>

***

### Withdrawal

| State                        | Hook Boolean             | Description                                   | Transitions Out                                                                                                                                                                                                                  |
| ---------------------------- | ------------------------ | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `withdraw:processing`        | `isWithdrawProcessing`   | Withdrawal executing on exchange              | `withdraw:completed`, `withdraw:error2FA`, `withdraw:error2FAMultiStep`, `withdraw:errorSMS`, `withdraw:errorKYC`, `withdraw:errorBalance`, `withdraw:retrying`, `withdraw:readyToConfirm`, `withdraw:blocked`, `withdraw:fatal` |
| `withdraw:error2FA`          | `requires2FA`            | Exchange requires single-step 2FA code        | `withdraw:processing` (via `submit2FA(code)`)                                                                                                                                                                                    |
| `withdraw:error2FAMultiStep` | `requires2FAMultiStep`   | Exchange requires multi-step MFA              | `withdraw:processing` (via `submit2FAMultiStep()` / `pollFaceVerification()`), `withdraw:readyToConfirm`                                                                                                                         |
| `withdraw:errorSMS`          | `requiresSMS`            | Exchange requires SMS verification            | `withdraw:processing` (via `submit2FA(code)`)                                                                                                                                                                                    |
| `withdraw:errorKYC`          | `requiresKYC`            | Exchange requires KYC completion              | Non-recoverable via SDK, direct user to exchange                                                                                                                                                                                 |
| `withdraw:errorBalance`      | `hasInsufficientBalance` | Insufficient balance for withdrawal           | Adjust amount, re-quote                                                                                                                                                                                                          |
| `withdraw:retrying`          | `canRetry`               | Auto-retry in progress                        | `withdraw:processing` (automatic)                                                                                                                                                                                                |
| `withdraw:readyToConfirm`    | `isReadyToConfirm`       | All 2FA steps verified, awaiting confirmation | `withdraw:processing` (via `confirmWithdrawal()`)                                                                                                                                                                                |
| `withdraw:completed`         | `isWithdrawalComplete`   | Withdrawal succeeded                          | Terminal (success)                                                                                                                                                                                                               |
| `withdraw:blocked`           | `isWithdrawBlocked`      | Exchange blocked the withdrawal               | Terminal, non-recoverable                                                                                                                                                                                                        |
| `withdraw:fatal`             | `hasFatalError`          | Unrecoverable withdrawal error                | Terminal                                                                                                                                                                                                                         |

**Compound booleans:**

* `isWithdrawing` = any `withdraw:*` state that is active (not completed, fatal, or error states)
* `requiresValid2FAMethod` = `withdraw:fatal` AND `errorDetails.valid2FAMethods` is present

<Warning>
  `withdraw:error2FA` and `withdraw:error2FAMultiStep` are **mutually exclusive** and **exchange-specific**. See [Error Handling & 2FA](/learn/sdk/error-handling) for the full breakdown.
</Warning>

***

### Flow Control

| State            | Hook Boolean      | Description                    | Transitions Out            |
| ---------------- | ----------------- | ------------------------------ | -------------------------- |
| `flow:cancelled` | `isFlowCancelled` | Flow was cancelled by the user | Terminal, start a new flow |

`cancel()` can be called from **any** state. It disposes the withdrawal machine and resets to `flow:cancelled`.

***

## Hook Booleans Quick Reference

### General

| Boolean           | State Check      | Phase        |
| ----------------- | ---------------- | ------------ |
| `isIdle`          | `idle`           | Idle         |
| `isFlowCancelled` | `flow:cancelled` | Flow Control |

### Exchanges

| Boolean              | State Check                                    |
| -------------------- | ---------------------------------------------- |
| `isExchangesLoading` | `exchanges:loading` OR standalone loading      |
| `isExchangesReady`   | `exchanges:ready` OR standalone data available |
| `isExchangesError`   | `exchanges:error`                              |

### OAuth

| Boolean                            | State Check                           |
| ---------------------------------- | ------------------------------------- |
| `isOAuthPending`                   | `oauth:waiting` OR `oauth:processing` |
| `isOAuthWaiting`                   | `oauth:waiting`                       |
| `isOAuthProcessing`                | `oauth:processing`                    |
| `isOAuthComplete`                  | `oauth:completed`                     |
| `isOAuthError`                     | `oauth:error` OR `oauth:fatal`        |
| `isOAuthFatal`                     | `oauth:fatal`                         |
| `isOAuthWindowBeenClosedByTheUser` | `oauth:window_closed_by_user`         |
| `isWalletConnectionInvalid`        | `oauth:fatal` OR `qrcode:fatal`       |

### QR Code

| Boolean              | State Check                                 |
| -------------------- | ------------------------------------------- |
| `isQRCodePending`    | Any `qrcode:*` except error, fatal, timeout |
| `isQRCodeWaiting`    | `qrcode:waiting`                            |
| `isQRCodeDisplaying` | `qrcode:displaying`                         |
| `isQRCodeScanning`   | `qrcode:scanning`                           |
| `isQRCodeError`      | `qrcode:error` OR `qrcode:fatal`            |
| `isQRCodeFatal`      | `qrcode:fatal`                              |
| `isQRCodeTimeout`    | `qrcode:timeout`                            |
| `isQRCodeFlow`       | Context: `isQRCodeFlow === true`            |

### Wallet

| Boolean                      | State Check                                                |
| ---------------------------- | ---------------------------------------------------------- |
| `isWalletLoading`            | `wallet:loading`                                           |
| `isWalletReady`              | `wallet:ready`                                             |
| `isWalletError`              | `wallet:error`                                             |
| `hasWalletNotFoundError`     | `wallet:error` + message contains "not found"              |
| `hasInvalidCredentialsError` | `wallet:error` + message contains "invalid" + "credential" |

### Quote

| Boolean           | State Check                                                                        |
| ----------------- | ---------------------------------------------------------------------------------- |
| `isQuoteLoading`  | `quote:requesting`                                                                 |
| `isQuoteReady`    | `quote:ready`                                                                      |
| `isQuoteExpired`  | `quote:expired`                                                                    |
| `isQuoteError`    | `quote:error`                                                                      |
| `hasAmountError`  | (`quote:error` OR `withdraw:fatal`) + message contains amount/minimum/maximum      |
| `hasAddressError` | (`quote:error` OR `withdraw:fatal`) + message contains address/invalid destination |
| `hasNetworkError` | (`quote:error` OR `withdraw:fatal`) + message contains network                     |

### Withdrawal

| Boolean                     | State Check                                                     |
| --------------------------- | --------------------------------------------------------------- |
| `isWithdrawing`             | Any active `withdraw:*` (not completed, fatal, or error states) |
| `isWithdrawProcessing`      | `withdraw:processing`                                           |
| `isWithdrawalComplete`      | `withdraw:completed`                                            |
| `isWithdrawBlocked`         | `withdraw:blocked`                                              |
| `hasFatalError`             | `withdraw:fatal`                                                |
| `requires2FA`               | `withdraw:error2FA`                                             |
| `requires2FAMultiStep`      | `withdraw:error2FAMultiStep`                                    |
| `isReadyToConfirm`          | `withdraw:readyToConfirm`                                       |
| `requiresSMS`               | `withdraw:errorSMS`                                             |
| `requiresKYC`               | `withdraw:errorKYC`                                             |
| `hasInsufficientBalance`    | `withdraw:errorBalance`                                         |
| `canRetry`                  | `withdraw:retrying`                                             |
| `requiresValid2FAMethod`    | `withdraw:fatal` + `errorDetails.valid2FAMethods` present       |
| `requiresEmailVerification` | `withdraw:fatal` + message contains "email"                     |

### Multi-Step 2FA

| Boolean                        | State Check                                             |
| ------------------------------ | ------------------------------------------------------- |
| `hasGoogleStep`                | `multiStep2FA.steps` contains type `GOOGLE`             |
| `hasEmailStep`                 | `multiStep2FA.steps` contains type `EMAIL`              |
| `hasFaceStep`                  | `multiStep2FA.steps` contains type `FACE`               |
| `hasSmsStep`                   | `multiStep2FA.steps` contains type `SMS`                |
| `isGoogleStepVerified`         | `mfa.verified.GOOGLE === true` OR step status `success` |
| `isEmailStepVerified`          | `mfa.verified.EMAIL === true` OR step status `success`  |
| `isFaceStepVerified`           | `mfa.verified.FACE === true` OR step status `success`   |
| `isSmsStepVerified`            | `mfa.verified.SMS === true` OR step status `success`    |
| `allMultiStep2FAStepsVerified` | All required steps have `mfa.verified[type] === true`   |

***

## Context Data Reference

The `flow.context` object (and shortcut properties on the hook return) carry data through the flow:

| Field                | Type                                                                                                                                         | Description                                                                   |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `exchanges`          | `Array<{ id, name, logoUrl, status }>`                                                                                                       | Available exchanges (also top-level `flow.exchanges`)                         |
| `walletBalances`     | `Array<{ asset, balance, balanceInFiat?, networks? }>`                                                                                       | Wallet balances with network details (also `flow.walletBalances`)             |
| `quote`              | `{ id, asset, amount, estimatedFee, estimatedTotal, amountWithFeeInFiat, amountNoFeeInFiat, estimatedFeeInFiat, additionalInfo, expiresAt }` | Current quote (also `flow.quote`)                                             |
| `withdrawal`         | `{ id, status, transactionId? }`                                                                                                             | Withdrawal result (also `flow.withdrawal`)                                    |
| `retryAttempts`      | `number`                                                                                                                                     | Current retry count (also `flow.retryAttempts`)                               |
| `maxRetryAttempts`   | `number`                                                                                                                                     | Max allowed retries, default 3 (also `flow.maxRetryAttempts`)                 |
| `invalid2FAAttempts` | `number`                                                                                                                                     | Consecutive invalid 2FA submissions (also `flow.invalid2FAAttempts`)          |
| `errorDetails`       | `{ valid2FAMethods?: string[] }`                                                                                                             | Additional error context                                                      |
| `qrCodeUrl`          | `string?`                                                                                                                                    | QR code URL to display (also `flow.qrCodeUrl`)                                |
| `qrCodeExpiresAt`    | `number?`                                                                                                                                    | QR code expiry timestamp in ms (also `flow.qrCodeExpiresAt`)                  |
| `multiStep2FA`       | `object?`                                                                                                                                    | Multi-step 2FA context, see [Error Handling & 2FA](/learn/sdk/error-handling) |

### Multi-Step 2FA Context

When `requires2FAMultiStep` is `true`, the `multiStep2FA` object is populated:

| Field                 | Type                                           | Description                                                                           |
| --------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------- |
| `bizNo`               | `string`                                       | Business number for the 2FA session (also `flow.multiStep2FABizNo`)                   |
| `steps`               | `Array<{ type, status, required, metadata? }>` | Verification steps (also `flow.multiStep2FASteps`)                                    |
| `relation`            | `'AND' \| 'OR'`                                | Whether all steps are required or any one suffices (also `flow.multiStep2FARelation`) |
| `collectedCodes`      | `{ twofa?, emailCode?, smsCode? }`             | Codes submitted so far (also `flow.collectedMultiStep2FACodes`)                       |
| `faceQrCodeUrl`       | `string?`                                      | QR code for FACE verification (also `flow.faceQrCodeUrl`)                             |
| `faceQrCodeExpiresAt` | `number?`                                      | FACE QR code expiry timestamp (also `flow.faceQrCodeExpiresAt`)                       |
| `mfa.verified`        | `{ GOOGLE?, EMAIL?, FACE?, SMS? }`             | Primary source of truth for step verification status (also `flow.mfaVerified`)        |

***

## Actions Reference

All actions are async callbacks returned by the `useBluvoFlow` hook.

| Action                       | Signature                                                                     | From States                                    | Effect                                                |
| ---------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------- |
| `listExchanges`              | `(status?: 'live' \| 'offline' \| 'maintenance' \| 'coming_soon') => Promise` | Any (standalone)                               | Fetches exchange list, sets `exchanges`               |
| `startWithdrawalFlow`        | `(options: WithdrawalFlowOptions) => Promise`                                 | `idle`, `exchanges:ready`                      | Starts OAuth/QR flow for the given exchange           |
| `resumeWithdrawalFlow`       | `(options: ResumeWithdrawalFlowOptions) => Promise`                           | Any                                            | Resumes a flow with a known wallet ID                 |
| `silentResumeWithdrawalFlow` | `(options: SilentResumeWithdrawalFlowOptions) => Promise`                     | Any                                            | Resumes silently (no error throw if wallet not found) |
| `requestQuote`               | `(options: QuoteRequestOptions) => Promise`                                   | `wallet:ready`, `quote:ready`, `quote:expired` | Requests a withdrawal quote                           |
| `executeWithdrawal`          | `(quoteId: string) => Promise`                                                | `quote:ready`                                  | Starts withdrawal execution                           |
| `submit2FA`                  | `(code: string) => Promise`                                                   | `withdraw:error2FA`                            | Submits single-step 2FA code                          |
| `submit2FAMultiStep`         | `(stepType: 'GOOGLE' \| 'EMAIL' \| 'SMS', code: string) => Promise`           | `withdraw:error2FAMultiStep`                   | Submits a code for one multi-step 2FA step            |
| `pollFaceVerification`       | `() => Promise`                                                               | `withdraw:error2FAMultiStep`                   | Polls server for FACE biometric verification status   |
| `confirmWithdrawal`          | `() => Promise`                                                               | `withdraw:readyToConfirm`                      | Confirms withdrawal after all 2FA steps verified      |
| `retryWithdrawal`            | `() => Promise`                                                               | `withdraw:retrying`                            | Triggers manual retry                                 |
| `refreshQRCode`              | `() => Promise`                                                               | `qrcode:error`, `qrcode:timeout`               | Requests a fresh QR code                              |
| `cancel`                     | `() => void`                                                                  | Any                                            | Cancels the flow, transitions to `flow:cancelled`     |

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Error Handling & 2FA" icon="triangle-exclamation" href="/learn/sdk/error-handling">
    Error recovery patterns, exchange-specific 2FA behavior, and multi-step MFA deep dive
  </Card>

  <Card title="OAuth2 Integration" icon="right-left" href="/learn/oauth2-integration">
    Step-by-step implementation guide with React and Next.js
  </Card>

  <Card title="Code Samples" icon="code" href="https://github.com/bluvoinc/awesome/">
    Full working examples for Next.js, React, and more
  </Card>

  <Card title="Wallet ID" icon="wallet" href="/learn/wallet-id">
    How to generate and manage wallet IDs for your users
  </Card>
</CardGroup>
