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

# In-chat flow

> A complete offramp conversation from login to settlement, step by step.

This page walks through a complete Veil offramp from inside a chat. Each step names the tool the assistant calls and shows what to expect.

The OTP confirmation step in this flow applies to every programmatic offramp surface, not only MCP — CLI and direct API use the same confirm gate. The dashboard uses session re-verification instead. See [Offramp lifecycle](/concepts/offramp-lifecycle) for the full picture.

## Prerequisites

* The Veil MCP connector is added and authorized in your client (see [Connect](/mcp/connect))
* Your Veil account has completed KYC and has a bank account added
* Your wallet holds USDC or USDT

If any of those aren't in place, the onboarding steps below cover them.

## Running an offramp

<Steps>
  <Step title="Check session">
    The assistant calls `veil_get_session_status` to see whether you are already logged in. If `status` is `authenticated`, skip to the account status step. If `unauthenticated`, continue to the next step.

    ```dialog theme={null}
    You: Send 50 USDC to my bank.
    Assistant: Let me check your Veil session first.
    [calls veil_get_session_status]
    → status: "unauthenticated"
    Assistant: You are not logged in. What email address should I use?
    ```
  </Step>

  <Step title="Request an OTP">
    The assistant calls `veil_request_otp` with your email. Veil sends a six-digit code to that address.

    ```dialog theme={null}
    You: you@example.com
    [calls veil_request_otp with email="you@example.com"]
    → status: "sent", requestId: "req_abc123"
    Assistant: A code was sent to you@example.com. Please share it when you receive it.
    ```
  </Step>

  <Step title="Verify the OTP">
    When you share the code, the assistant calls `veil_verify_otp`. On success, your session is active and the assistant has your user profile.

    ```dialog theme={null}
    You: 482910
    [calls veil_verify_otp with requestId="req_abc123", otp="482910"]
    → status: "logged_in", user.email: "you@example.com"
    Assistant: Logged in. Let me check your account status.
    ```
  </Step>

  <Step title="Check account status">
    The assistant calls `veil_get_account_status`. This tells it whether KYC is approved and a bank account is present. If `stage` is `ready`, skip to the wallet step.

    ```dialog theme={null}
    [calls veil_get_account_status]
    → stage: "ready", kyc.status: "approved", bankAccount.present: true
    ```

    If `stage` is `needs_kyc`, the assistant calls `veil_kyc_start` and shares a link for you to open in a browser. After you complete the form, it polls `veil_kyc_check_status` until `status` is `approved`.

    If `stage` is `needs_bank_account`, the assistant calls `veil_bank_account_get_form_url` and shares a link. You fill in your bank details in the browser, then return to the chat.
  </Step>

  <Step title="List wallets">
    The assistant calls `veil_wallets_list` to find your wallets and their USDC/USDT balances.

    ```dialog theme={null}
    [calls veil_wallets_list]
    → wallets: [{ id: "wal_xyz", name: "Base wallet", chain: "base", balance: { USDC: "120.00" } }]
    ```
  </Step>

  <Step title="Get a quote">
    The assistant calls `veil_offramp_quote` with the amount, asset, wallet, and destination bank. The quote is non-binding — you can adjust the amount and re-quote as many times as you like.

    ```dialog theme={null}
    Assistant: Your Base wallet holds 120 USDC. How much would you like to send?
    You: 50 USDC
    [calls veil_offramp_quote with amount=50, assetIn="USDC", walletId="wal_xyz"]
    → amountOut: 46.23, currencyOut: "EUR", exchangeRate: 0.9246, totalFee: 0.46
    Assistant: You will receive approximately €46.23 after fees. Shall I proceed?
    ```
  </Step>

  <Step title="Execute the offramp">
    After you confirm the quote, the assistant calls `veil_offramp_execute`. This creates a pending transaction and, when a code is required, sends a six-digit confirmation code to your email.

    ```dialog theme={null}
    You: Yes, go ahead.
    [calls veil_offramp_execute with walletId="wal_xyz", amount=50, assetIn="USDC", userConfirmed=true]
    → status: "ok", transactionId: "txn_123", requestId: "req_456", maskedEmail: "y***@example.com"
    Assistant: A confirmation code was sent to y***@example.com. Please share it to release the funds.
    ```

    <Note>
      The assistant requires `userConfirmed=true` before calling `veil_offramp_execute`. It will not proceed without your explicit approval.
    </Note>

    <Note>
      A code isn't always required. Depending on your offramp security settings, a send to your own bank may settle immediately — the response returns the transaction with no `requestId`, and the next confirm step is skipped. Sends to a `@username` or a new recipient always require a code, and sandbox never does.
    </Note>
  </Step>

  <Step title="Confirm with the code">
    Share the six-digit code from your email. The assistant calls `veil_offramp_confirm`. This step is irreversible — once confirmed, the crypto is converted and the fiat is sent to your bank.

    ```dialog theme={null}
    You: 719302
    [calls veil_offramp_confirm with transactionId="txn_123", requestId="req_456", code="719302"]
    → status: "ok", transaction.status: "processing"
    Assistant: Confirmed. The offramp is processing. EUR settlement typically arrives the same day.
    ```
  </Step>

  <Step title="Track status (optional)">
    To check progress later, the assistant calls `veil_offramp_get_transaction` with the transaction ID, or `veil_transactions_list` to see recent activity.

    ```dialog theme={null}
    You: What happened to my offramp?
    [calls veil_offramp_get_transaction with transactionId="txn_123"]
    → transaction.status: "completed", settledAt: "2026-05-14T14:22:00Z"
    Assistant: Your offramp completed and the fiat was sent to your bank.
    ```
  </Step>
</Steps>

## Sending to a Veil user by @handle

If the recipient has a Veil username, you can send directly to their `@handle`. The assistant calls `veil_username_beneficiary_preview` first to confirm the recipient is ready, then passes the binding token to `veil_offramp_quote` and `veil_offramp_execute`.

```dialog theme={null}
You: Send 30 USDC to @alice.
[calls veil_username_beneficiary_preview with handle="alice"]
→ status: "ready", displayUsername: "@alice", bindingHint: "Use bindingToken=..."
[calls veil_offramp_quote with amount=30, assetIn="USDC", beneficiaryUsername="alice", bindingToken=...]
→ amountOut: 27.71, currencyOut: "EUR"
```

The sender never sees the recipient's bank details. See [Usernames](/concepts/usernames).

## See also

* [Environments](/mcp/environments) — switching between sandbox and live modes
* [Tool reference](/mcp/tools/index) — all 31 tools with full input/output documentation
* [Offramp lifecycle](/concepts/offramp-lifecycle) — the full lifecycle across all surfaces
