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

# Offramp tools

> Quote, execute, confirm, and track offramp transactions via MCP.

Five tools cover the full offramp lifecycle. Read the [Offramp lifecycle](/concepts/offramp-lifecycle) concept page for the sequence overview.

***

### veil\_offramp\_quote

Get a non-binding exchange rate quote for converting crypto to fiat. Call this before `veil_offramp_execute` to show the user what they will receive.

**Inputs**

<ParamField body="amount" type="number" required>
  Amount of crypto to convert, in whole units (e.g. `50` for 50 USDC).
</ParamField>

<ParamField body="assetIn" type="string" required>
  Token to convert. One of `USDC` or `USDT`.
</ParamField>

<ParamField body="walletId" type="string">
  Optional. When provided, the tool verifies the wallet holds at least `amount` of `assetIn` before calling the upstream quote service. Fails fast on over-balance requests.
</ParamField>

<ParamField body="bankAccountId" type="string">
  Optional. Route the quote to a specific bank account. Omit to use the wallet's primary bank. Mutually exclusive with `beneficiaryUsername`.
</ParamField>

<ParamField body="beneficiaryUsername" type="string">
  Optional. Quote a send-to-handle offramp. Pass the `@handle` without the `@` prefix. Mutually exclusive with `bankAccountId`.
</ParamField>

<ParamField body="bindingToken" type="string">
  Optional. Obtained from `veil_username_beneficiary_preview`'s `bindingHint` field. Locks the recipient and prevents drift between preview and execute.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `ok` on success, `error` on failure.
</ResponseField>

<ResponseField name="amountIn" type="number">
  Amount of crypto sent.
</ResponseField>

<ResponseField name="assetIn" type="string">
  Token sent (`USDC` or `USDT`).
</ResponseField>

<ResponseField name="amountOut" type="number">
  Estimated fiat amount to receive.
</ResponseField>

<ResponseField name="currencyOut" type="string">
  Fiat currency (e.g. `EUR`, `USD`).
</ResponseField>

<ResponseField name="exchangeRate" type="number">
  Exchange rate (fiat per crypto unit).
</ResponseField>

<ResponseField name="noahFee" type="number">
  Processing fee in destination currency.
</ResponseField>

<ResponseField name="totalFee" type="number">
  Total fee in destination currency.
</ResponseField>

<ResponseField name="bankAccountId" type="string">
  The bank account that will receive the funds.
</ResponseField>

<ResponseField name="suffixLast4" type="string">
  Last four digits of the destination account for confirmation.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_offramp_quote with amount=50, assetIn="USDC", walletId="wal_xyz"]
→ status: "ok", amountIn: 50, assetIn: "USDC", amountOut: 46.23,
  currencyOut: "EUR", exchangeRate: 0.9246, totalFee: 0.46,
  bankAccountId: "ba_abc", suffixLast4: "1234"
```

When quoting against a `beneficiaryUsername` instead of a `bankAccountId`, the response carries the resolved destination from the binding preview — the recipient's `bankAccountId` and `suffixLast4` reflect their primary bank, not the sender's.

***

### veil\_offramp\_execute

Initiate an offramp. When a confirmation code is required, it creates a pending transaction, emails a six-digit code (expires in 10 minutes), and returns a `requestId` for `veil_offramp_confirm`. When the user's offramp security policy waives the code — sandbox, or an own-bank send they've turned off or set below their threshold — it settles immediately and returns the transaction directly, with no `requestId` and no confirm step. Sends to a `@username` or a one-off recipient always require the code.

The user manages this policy in the dashboard, under **Settings → Offramp security**. There's no MCP tool to read or change it — the MCP server only honors it. If a user asks to adjust when codes are required, point them to the dashboard.

<Note>
  Call this only after the user has reviewed the quote and explicitly confirmed they want to proceed. The `userConfirmed` field must be `true`.
</Note>

**Inputs**

<ParamField body="walletId" type="string" required>
  ID of the wallet to debit.
</ParamField>

<ParamField body="amount" type="number" required>
  Amount to convert.
</ParamField>

<ParamField body="assetIn" type="string" required>
  Token to convert. One of `USDC` or `USDT`.
</ParamField>

<ParamField body="userConfirmed" type="boolean" required>
  Must be `true`. The assistant will not call this tool without explicit user approval.
</ParamField>

<ParamField body="bankAccountId" type="string">
  Optional. Destination bank account. Omit to use the wallet's primary bank. Mutually exclusive with `beneficiaryUsername`.
</ParamField>

<ParamField body="beneficiaryUsername" type="string">
  Optional. Send to a `@handle` recipient. Mutually exclusive with `bankAccountId`.
</ParamField>

<ParamField body="bindingToken" type="string">
  Optional. From `veil_username_beneficiary_preview`. Locks the recipient.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `ok` on success, `error` on failure.
</ResponseField>

<ResponseField name="transactionId" type="string">
  Transaction ID. Pass to `veil_offramp_confirm`.
</ResponseField>

<ResponseField name="requestId" type="string">
  Confirmation request ID. Pass to `veil_offramp_confirm`.
</ResponseField>

<ResponseField name="amountIn" type="number">
  Amount of crypto being converted.
</ResponseField>

<ResponseField name="assetIn" type="string">
  Token being converted.
</ResponseField>

<ResponseField name="amountOut" type="number">
  Fiat amount to receive.
</ResponseField>

<ResponseField name="currencyOut" type="string">
  Fiat currency.
</ResponseField>

<ResponseField name="fee" type="number">
  Fee applied.
</ResponseField>

<ResponseField name="exchangeRate" type="number">
  Rate locked at execute time.
</ResponseField>

<ResponseField name="confirmationExpiresAt" type="string">
  ISO 8601 timestamp when the confirmation code expires.
</ResponseField>

<ResponseField name="maskedEmail" type="string">
  Masked email where the code was sent (e.g. `y***@example.com`).
</ResponseField>

<ResponseField name="message" type="string">
  Human-readable instruction for the next step (e.g. prompting the user for the confirmation code).
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[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", amountOut: 46.23, currencyOut: "EUR",
  message: "Ask the user for the 6-digit code sent to their email."
```

***

### veil\_offramp\_confirm

Confirm an offramp by submitting the six-digit code sent to the user's email. This step is irreversible.

**Inputs**

<ParamField body="transactionId" type="string" required>
  From `veil_offramp_execute`.
</ParamField>

<ParamField body="requestId" type="string" required>
  From `veil_offramp_execute`.
</ParamField>

<ParamField body="code" type="string" required>
  The six-digit code from the user's email.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `ok` on success, `error` on failure.
</ResponseField>

<ResponseField name="transaction" type="object">
  Full transaction record. See `veil_offramp_get_transaction` for the shape.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`. Common values: `Invalid confirmation code`, `Confirmation code has expired`, `Exchange rate changed`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_offramp_confirm with transactionId="txn_123", requestId="req_456", code="719302"]
→ status: "ok", transaction.status: "processing", transaction.currencyOut: "EUR"
```

***

### veil\_offramp\_get\_transaction

Get the current status and details of a specific transaction.

**Inputs**

<ParamField body="transactionId" type="string" required>
  Transaction ID from `veil_offramp_execute` or `veil_transactions_list`.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `ok` on success, `error` on failure.
</ResponseField>

<ResponseField name="transaction" type="object">
  Transaction record with fields: `id`, `type`, `status` (`pending` / `processing` / `completed` / `failed`), `amountIn`, `assetIn`, `amountOut`, `currencyOut`, `fee`, `exchangeRate`, `wireRef`, `createdAt`, `settledAt`, `wallet.name`, `wallet.chain`, `bankAccount.bankName`, `bankAccount.suffixLast4`, `bankAccount.currency`. The `bankAccount.*` fields are `null` when the viewer is not the account owner (e.g. on the sender side of a `@handle` offramp).
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_offramp_get_transaction with transactionId="txn_123"]
→ status: "ok", transaction.status: "completed", transaction.settledAt: "2026-05-14T14:22:00Z"
```

***

### veil\_transactions\_list

List offramp and deposit transactions with pagination and filtering.

**Inputs**

<ParamField body="limit" type="number">
  Number of results per page. Maximum 100. Server applies a default if omitted.
</ParamField>

<ParamField body="offset" type="number">
  Pagination offset. Server defaults to 0 if omitted.
</ParamField>

<ParamField body="status" type="string">
  Optional filter. One of `pending`, `processing`, `completed`, `failed`.
</ParamField>

<ParamField body="type" type="string">
  Optional filter. One of `offramp`, `deposit`.
</ParamField>

**Outputs**

<ResponseField name="status" type="string" required>
  `ok` on success, `error` on failure.
</ResponseField>

<ResponseField name="transactions" type="array">
  Array of transaction records. Each has the same shape as `veil_offramp_get_transaction`'s `transaction` field.
</ResponseField>

<ResponseField name="total" type="number">
  Total number of matching transactions.
</ResponseField>

<ResponseField name="hasMore" type="boolean">
  Whether another page is available.
</ResponseField>

<ResponseField name="limit" type="number">
  Limit applied to this response.
</ResponseField>

<ResponseField name="offset" type="number">
  Offset applied to this response.
</ResponseField>

<ResponseField name="reason" type="string">
  Error description when `status` is `error`.
</ResponseField>

**Example**

```dialog theme={null}
[calls veil_transactions_list with status="completed", limit=5]
→ status: "ok", total: 12, hasMore: true, transactions: [...]
```

## See also

* [In-chat flow](/mcp/in-chat-flow) — how these tools fit into a conversation
* [Offramp lifecycle](/concepts/offramp-lifecycle) — quote → execute → confirm → settle
