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

# Headless bridging

By the end of this guide, you'll know how to use Mesh's headless bridging APIs to discover bridged routes and generate a bridging address — so you can offer users far more asset/network options (for deposit or withdrawal) than you natively support, without building, hosting, or embedding any Mesh UI.

<Check>
  **Before you start**

  * Your core Mesh integration is working end-to-end (see [Prepare for go-live](/build/go-live))
  * **Headless bridging is enabled for your Mesh client.** This is gated per-client — contact your Mesh representative to turn it on.
  * The origin and destination asset/network pairs you want to bridge have been configured as routes for your account — your Mesh representative sets these up.
</Check>

## Overview

Headless bridging gives you Mesh's cross-chain bridging engine as a pure API — **no Mesh-hosted UI, no iframe, no SDK**. You keep your own front end; Mesh does three things for you:

1. **Route discovery** — tells you every asset/network pair that Mesh can bridge into or out of the pairs you support, so you can offer them on top of your native options. These pairs can be offered in a deposit flow or withdrawal flow.
2. **Quote** *(optional)* — for a given pay-in amount on a bridged pair, returns the estimated amount the recipient will receive after bridging fees, so you can show it before anything is sent.
3. **Address generation** — returns a single-use address that is programmed to bridge funds from a pay-in pair into the destination pair (and address) the user selected.

This lets you present users with many more options while you keep full control of the experience.

<Info>
  **Native vs. bridged pairs**

  A **native** pair is one your platform already sends or receives directly. A **bridged** pair is one Mesh makes possible by bridging on your behalf. Route discovery returns every bridged route Mesh can offer into (deposit) or out of (withdrawal) the pairs you send; Mesh does not know which pairs you support natively, so results can include routes whose other leg you already support. Filter those against your own list before showing options to the user. Only bridged pairs use the bridging APIs (`/api/v1/bridging/*`); handle native pairs the way you do today, whether that is your own rails or an existing Mesh flow.
</Info>

## How it works

The whole flow is two required API calls plus an optional quote, and everything after route discovery only happens for bridged pairs:

1. **You call Mesh — route discovery.** You send the asset/network pairs you support (as origins for a withdrawal, or destinations for a deposit). Mesh returns the bridged routes it can offer into (deposit) or out of (withdrawal) those pairs.
2. **You show the user their options** — your native pairs plus the bridged pairs from step 1.
3. **The user selects a destination:**
   * **If native** → no second Mesh call. You handle it exactly as you do today.
   * **If bridged** → optionally call Mesh to **get a quote** for the amount the user entered, and show them the estimated receive amount. Once they confirm, call Mesh to **create a bridged address**. For user deposits, you'll show the user that address to which they can send the pay-in funds. For user withdrawals, you'll send the pay-in to that address. Mesh bridges the funds to the specified pair and address.

All endpoints live on the Mesh B2B API under `/api/v1/bridging`, use JSON with `camelCase` fields, and authenticate with your `X-Client-Id` and `X-Client-Secret` headers on every request.

<Info>
  **Seeing `permissionDenied`?** Until headless bridging is enabled for your client, every headless bridging call described on this page returns HTTP `403` with `"status": "permissionDenied"` and the message `Headless bridging is not enabled for this client`. That is the expected response, not a malformed request. Contact your Mesh representative to have it turned on.
</Info>

<Warning>
  Bridging executes **on mainnet**. A generated address is a real, live address, and any pay-in moves real funds — there is no testnet simulation of the bridge itself. Test with small amounts.
</Warning>

## Step 1 — Discover routes

Route discovery comes in two shapes depending on your flow. Both return the same response structure.

* **Withdrawal** (`POST /api/v1/bridging/routes/withdrawal`) — you send `sources` (the pairs your platform can pay out / send from). Mesh returns the destinations you can bridge those into.
* **Deposit** (`POST /api/v1/bridging/routes/deposit`) — you send `destinations` (the pairs you can ultimately receive). Mesh returns the origins a user could pay in from.

<div className="parameter-reference">
  <Accordion title="Parameter reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>X-Client-Id</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh Client ID. Requires **read** access.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>X-Client-Secret</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh API Key.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>sources</code> *(withdrawal)* <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Non-empty array of the pairs you can send from. Each entry: `network` (CAIP-2 chain id, e.g. `eip155:1`) and `asset` (symbol, case-insensitive, e.g. `USDC`).</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destinations</code> *(deposit)* <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Non-empty array of the pairs you can receive. Same `{ network, asset }` shape as `sources`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>optimized</code> <span className="param-tag param-tag-optional">optional</span></div>

        <div className="param-body">
          <p>Defaults to `true`. When `true`, Mesh returns at most one *source* per distinct *destination* (withdrawal) or at most one *destination* per distinct *source* (deposit), choosing the preferred counterpart using a Mesh-configured network priority. Set to `false` to return every matching origin → destination pair.</p>
          <p>With a single settlement source (e.g. Polygon USDC), `optimized: true` still returns every available destination.</p>
        </div>
      </div>
    </div>
  </Accordion>
</div>

```bash theme={null}
# Replace YOUR_CLIENT_ID and YOUR_API_KEY with your Mesh credentials.
# Replace the sources entries with the pairs you settle in.
# eip155:137 + USDC is USDC on Polygon.
curl --request POST \
  --url https://integration-api.meshconnect.com/api/v1/bridging/routes/withdrawal \
  --header 'Content-Type: application/json' \
  --header 'X-Client-Id: YOUR_CLIENT_ID' \
  --header 'X-Client-Secret: YOUR_API_KEY' \
  --data '
{
  "sources": [
    { "network": "eip155:137", "asset": "USDC" }
  ],
  "optimized": true
}
'
```

Each item in `routes` is an `origin → destination` pair you can offer. Use `status` on the envelope to confirm success; `routes` may be empty when no bridged pairs match.

Do not branch on `status` alone. An unknown network, or a pair with no configured route, is not an error here: you get `status: "ok"` with an empty `routes` array. Schema-level failures, such as a missing required field or an empty `sources` / `destinations` array, come back in the same envelope with `status: "badRequest"` and `errorType: "invalidField"` (or `"missingField"` when a required field is omitted), with the offending field named in `message`.

```json theme={null}
{
  "status": "ok",
  "content": {
    "routes": [
      {
        "origin": { "network": "eip155:137", "asset": "USDC" },
        "destination": { "network": "eip155:1", "asset": "USDC" },
        "displayName": "USDC on Polygon → USDC on Ethereum"
      }
    ]
  }
}
```

## Step 2 — Get a quote (optional)

Whenever you know the pay-in amount before the transfer, in either flow, call `POST /api/v1/bridging/quote` with the `origin` and `destination` from the selected route, the payout `address`, and the `amount`. For a withdrawal that is the amount the user asked to withdraw; for a deposit it is the amount the user told you they will send. Mesh returns the estimated amount the recipient will receive after bridging fees, plus the fee lines behind it. Nothing is created or persisted, so call it as often as the user changes the amount. Requires **read** access.

<Info>
  **The quote is an estimate.** The bridging address from Step 3 accepts any amount and the bridge is priced on what actually arrives, so fees and rates can move between quoting and settlement. Show `estimatedAmount` as an estimate and surface `warnings` to the user. Skip this step only when you have no amount to quote, for example a deposit where the user decides the amount in their own wallet after seeing the address.
</Info>

<div className="parameter-reference">
  <Accordion title="Parameter reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>X-Client-Id</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh Client ID. Requires **read** access.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>X-Client-Secret</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh API Key.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>origin.<strong>network</strong></code> / <code>origin.<strong>asset</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">The pay-in leg, taken from the selected route's `origin`. Same shape as in Step 3.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>network</strong></code> / <code>destination.<strong>asset</strong></code> / <code>destination.<strong>address</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">The payout leg and recipient wallet, taken from the selected route's `destination` plus the address you will pass to Step 3.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>amount</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Decimal amount to be sent, in the **origin asset** (e.g. `100` for 100 USDC). Must be greater than zero and at most 1,000,000,000.</div>
      </div>
    </div>
  </Accordion>
</div>

```bash theme={null}
# Replace YOUR_CLIENT_ID and YOUR_API_KEY with your Mesh credentials.
# origin and destination come from the route the user selected.
curl --request POST \
  --url https://integration-api.meshconnect.com/api/v1/bridging/quote \
  --header 'Content-Type: application/json' \
  --header 'X-Client-Id: YOUR_CLIENT_ID' \
  --header 'X-Client-Secret: YOUR_API_KEY' \
  --data '
{
  "origin": {
    "network": "eip155:137",
    "asset": "USDC"
  },
  "destination": {
    "network": "eip155:1",
    "asset": "USDC",
    "address": "0x1234567890123456789012345678901234567890"
  },
  "amount": 100
}
'
```

Show `destination.estimatedAmount` to the user as the estimated receive amount. `fees.items` explains where the difference went.

```json theme={null}
{
  "status": "ok",
  "content": {
    "origin": { "network": "eip155:137", "asset": "USDC", "amount": 100 },
    "destination": {
      "network": "eip155:1",
      "asset": "USDC",
      "address": "0x1234567890123456789012345678901234567890",
      "estimatedAmount": 98.7,
      "minimumAmount": 96.7
    },
    "fees": {
      "items": [
        {
          "kind": "providerRelayerGas",
          "label": "Relayer gas fee",
          "amount": 1.1,
          "asset": "USDC",
          "amountUsd": 1.1,
          "includedInEstimate": true
        },
        {
          "kind": "providerRelayerService",
          "label": "Relayer service fee",
          "amount": 0.2,
          "asset": "USDC",
          "amountUsd": 0.2,
          "includedInEstimate": true
        }
      ],
      "totalUsd": 1.3
    },
    "rate": 0.987,
    "quotedAt": 1791000000,
    "warnings": [
      "This quote is an estimate. The deposit address returned by POST /api/v1/bridging/address accepts any amount and the bridge is priced on the amount that actually arrives; fees and rates may change between quoting and settlement."
    ]
  }
}
```

<div className="parameter-reference">
  <Accordion title="Response reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>origin.<strong>amount</strong></code></div>
        <div className="param-body">The pay-in amount the quote was priced for, in the origin asset.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>estimatedAmount</strong></code></div>
        <div className="param-body">Estimated amount the recipient receives in the destination asset, net of the fees marked `includedInEstimate: true`. Indicative only.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>minimumAmount</strong></code></div>
        <div className="param-body">The provider's indicative minimum for *this* quote at Mesh's configured slippage tolerance. May be `null`. The quote is not bound to the later `/address` call, which obtains a fresh quote that may differ, so treat it as a snapshot rather than a committed floor for settlement.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>fees.<strong>items\[]</strong></code></div>
        <div className="param-body">Fee lines from the bridge provider: `kind`, `label`, `amount` and `asset` (the currency the fee is charged in), `amountUsd`, and `includedInEstimate`. Lines with `includedInEstimate: false` (e.g. `originGas`) are informational and are paid by the sender, not deducted from the payout.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>fees.<strong>totalUsd</strong></code></div>
        <div className="param-body">Total USD value of the fees deducted from the payout. May be `null`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>rate</code></div>
        <div className="param-body">Destination units per one origin unit, net of fees (`estimatedAmount / amount`).</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>quotedAt</code></div>
        <div className="param-body">Unix seconds when the quote was produced. Re-quote if the user lingers before confirming.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>warnings</code></div>
        <div className="param-body">Human-readable caveats to show alongside the estimate.</div>
      </div>
    </div>
  </Accordion>
</div>

Common errors: `errorType: "badRequest"` for an unknown network or no configured route for the pair; `errorType: "amountBelowMinimum"` when the amount is under the minimum Mesh has configured for the route, or when the provider will not bridge that little (raise the amount and re-quote); `errorType: "amountExceedsMaximum"` when the amount is above the provider's available liquidity for the route (the `message` includes the current maximum in USD; lower the amount and re-quote); `permissionDenied` when headless bridging is not enabled for your client. Schema violations, such as a missing field, an `amount` outside the allowed range, or an `address` over 200 characters, come back as `errorType: "invalidField"` or `"missingField"` with the field named in `message`.

## Step 3 — Create a bridged address

When a user selects a **bridged** pair (and has confirmed the quoted estimate, if you showed one), call this endpoint with the route's `origin` and `destination`, plus the payout `address`: the user's wallet on a withdrawal, or your platform receive address on a deposit. Mesh returns a single-use deposit address to send the pay-in to.

<div className="parameter-reference">
  <Accordion title="Parameter reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>X-Client-Id</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh Client ID. Requires **write** access.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>X-Client-Secret</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your Mesh API Key.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>userId</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your unique, persistent identifier for the user (max 384 characters). Do not use PII such as an email or phone number.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>transactionId</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Your unique transfer/order id (max 128 characters). `userId` + `transactionId` identify the transfer: while it is still in progress, repeating the call with the same `origin`, `destination`, and `address` returns the same bridging address, and changing any of those returns `400 badRequest`. Once the transfer has completed, or the earlier address has expired and been cleaned up, the same `transactionId` creates a new address.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>origin.<strong>network</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">CAIP-2 chain id of the pay-in leg (the pair you'll send from). Taken from the selected route's `origin`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>origin.<strong>asset</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Asset symbol of the pay-in leg. Taken from the selected route's `origin`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>network</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">CAIP-2 chain id of the payout leg. Taken from the selected route's `destination`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>asset</strong></code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Asset symbol of the payout leg. Taken from the selected route's `destination`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>destination.<strong>address</strong></code> <span className="param-tag param-tag-required">required</span></div>

        <div className="param-body">
          <p>The payout address on the destination chain. On a **withdrawal** this is the user's destination wallet. On a **deposit** it is your platform's receive address on that chain.</p>
          <p>The format must match that chain (e.g. `0x…` for EVM, base58 for Solana, `bc1…` / Bitcoin bech32 for BTC). Use the same address you quoted with in Step 2.</p>
        </div>
      </div>
    </div>
  </Accordion>
</div>

```bash theme={null}
# Replace YOUR_CLIENT_ID and YOUR_API_KEY with your Mesh credentials.
# Replace userId and transactionId with your own identifiers.
# origin is the pair you send from: eip155:137 + USDC is USDC on Polygon.
# destination is the route the user selected, plus the payout address:
# the user's wallet on a withdrawal, your platform's receive address on a deposit.
curl --request POST \
  --url https://integration-api.meshconnect.com/api/v1/bridging/address \
  --header 'Content-Type: application/json' \
  --header 'X-Client-Id: YOUR_CLIENT_ID' \
  --header 'X-Client-Secret: YOUR_API_KEY' \
  --data '
{
  "userId": "UNIQUE_USER_ID",
  "transactionId": "UNIQUE_TRANSACTION_ID",
  "origin": {
    "network": "eip155:137",
    "asset": "USDC"
  },
  "destination": {
    "network": "eip155:1",
    "asset": "USDC",
    "address": "0x1234567890123456789012345678901234567890"
  }
}
'
```

Send the pay-in (the `origin` asset on the `origin` network **only**) to `selectedRoute.bridgingAddress.address`. Mesh bridges it to the `destination`.

This endpoint does not take an amount and does not return a quoted `amountOut`. Mesh uses an internal probe quote, so you can send whatever size you intend, subject to the bridge provider's floors (undersized pay-ins can still fail, most often on BTC). Fees reduce what finally arrives, and the received amount is not part of this response.

```json theme={null}
{
  "status": "ok",
  "content": {
    "tracking": {
      "userId": "UNIQUE_USER_ID",
      "transactionId": "UNIQUE_TRANSACTION_ID",
      "transferId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
    },
    "selectedRoute": {
      "origin": { "network": "eip155:137", "asset": "USDC" },
      "bridgingAddress": { "address": "0xbridge000000000000000000000000000000000001" },
      "destination": {
        "network": "eip155:1",
        "asset": "USDC",
        "address": "0x1234567890123456789012345678901234567890"
      }
    },
    "constraints": {
      "minimumAmount": "0",
      "maximumAmount": null,
      "expirationTime": 1893456000,
      "singleUseAddress": true
    },
    "warnings": [
      "Single-use address only — do not reuse this address.",
      "Funds must be sent on the selected source network (Polygon).",
      "Fees may reduce the final received amount."
    ]
  }
}
```

<div className="parameter-reference">
  <Accordion title="Response reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>tracking.<strong>transferId</strong></code></div>
        <div className="param-body">Mesh transfer id (UUID). Use it to track status and match webhooks (see **Track the bridge** below).</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>selectedRoute.bridgingAddress.<strong>address</strong></code></div>
        <div className="param-body">The address to display for the pay-in. Send only the `origin` asset on the `origin` network here.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>constraints.<strong>minimumAmount</strong></code> / <code>constraints.<strong>maximumAmount</strong></code></div>

        <div className="param-body">
          <p>Decimal strings in the *origin* asset. `maximumAmount` is currently always `null`.</p>
          <p>`minimumAmount` is a configured display hint (often `"0"`). Mesh does not block a smaller pay-in. Relay may still fail undersized amounts at quote/fill time (e.g. BTC). Enforce this client-side before showing the address.</p>
          <p>If Mesh has a minimum configured, `warnings` also includes `Minimum transfer amount: {n} {ASSET}.`</p>
        </div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>constraints.<strong>expirationTime</strong></code></div>
        <div className="param-body">Unix seconds after which the address should not be used. May be `null`.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>constraints.<strong>singleUseAddress</strong></code></div>
        <div className="param-body">When `true`, the address is for this one transfer only — never reuse it for another pay-in.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>warnings</code></div>
        <div className="param-body">Human-readable strings to surface to the user before they send the pay-in.</div>
      </div>
    </div>
  </Accordion>
</div>

<Warning>
  Addresses are **single-use**. Generate a fresh one (a new `transactionId`) for every pay-in, and never route a second transfer to a previously used address.
</Warning>

Errors are all in the envelope. An idempotency mismatch, meaning the same `userId` and `transactionId` sent with a different `origin`, `destination`, or `address` while the transfer is still in progress, returns `errorType: "badRequest"`. Schema-level failures, such as a missing required field or a `destination.address` over 200 characters, return `errorType: "invalidField"` (or `"missingField"`) with the field named in `message`.

## Track the bridge

Each bridged transfer returns a Mesh `transferId`. Use it to reconcile the transfer and to match Mesh Transfer Status webhooks as the bridge moves from pending to complete. Register your webhook endpoint in the Mesh dashboard under **Account > API keys > Webhooks**, and see the [Transfer status webhooks](/resources/webhooks) guide for payload details.

You can also poll [Get transfers initiated by Mesh](/api-reference/managed-transfers/get-transfers-initiated-by-mesh) (`GET /api/v1/transfers/managed/mesh`) with `Id` set to `tracking.transferId`. Status can lag the on-chain fill by a few minutes, and BTC fills can stay pending well past the quote ETA without an error.

## What's next

Explore related guides:

* [Add Mesh to your withdrawal flow](/extend/withdrawal) — the withdrawal-flow guide, which includes a section on using headless bridging to expand withdrawal destinations.
* [Transfer status webhooks](/resources/webhooks) — register for and consume transfer status webhooks to track your bridges.

***

<Accordion title="AI coding reference (llms.txt)">
  *AI coding reference — a compact summary of this page's APIs, parameters, and patterns for use by AI coding assistants (following the [llms.txt standard](https://llmstxt.org/)). Human readers can safely ignore this.*

  **llms.txt — Headless bridging**

  Headless bridging = Mesh's cross-chain bridging as a pure API (no Mesh UI/SDK/iframe). Three steps, the middle one optional: (1) route discovery returns every bridged route Mesh can offer into (deposit) or out of (withdrawal) the pairs you send — it does not know your native pairs, so filter overlaps client-side; (2) quote returns the estimated receive amount for a given pay-in amount on a pair (stateless, indicative); (3) address generation returns a single-use deposit address programmed to bridge a pay-in pair into a user-selected destination pair + address. All endpoints under `/api/v1/bridging`, JSON camelCase, auth via `X-Client-Id` + `X-Client-Secret` headers on every request. Gated per-client — must be enabled by Mesh. Bridging executes on mainnet (no testnet simulation).

  **Route discovery — withdrawal**: `POST /api/v1/bridging/routes/withdrawal`. Body: `sources` (required, non-empty array of `{ network (CAIP-2), asset (symbol) }` you can send from) | `optimized` (optional, default true — withdrawal returns at most one source per distinct destination, deposit at most one destination per distinct source; false = every matching origin→destination pair. With a single settlement source, true still returns every destination). Response: `content.routes[]` of `{ origin, destination, displayName }`.

  **Route discovery — deposit**: `POST /api/v1/bridging/routes/deposit`. Same as withdrawal but send `destinations` (pairs you can receive) instead of `sources`. Response identical shape.

  **Quote (optional)**: `POST /api/v1/bridging/quote` (read access). Body: `origin` `{ network, asset }` | `destination` `{ network, asset, address }` | `amount` (decimal, in the origin asset, greater than zero and at most 1,000,000,000). Response: `content.origin.amount` | `content.destination.estimatedAmount` (net of included fees, indicative) | `content.destination.minimumAmount` (provider's indicative minimum for this quote, nullable; not bound to the later `/address` quote) | `content.fees.items[]` `{ kind, label, amount, asset, amountUsd, includedInEstimate }` | `content.fees.totalUsd` | `content.rate` | `content.quotedAt` (unix secs) | `content.warnings[]`. Stateless — nothing minted or stored; call on every amount change. Errors: `badRequest` (unknown network / no route), `amountBelowMinimum` (below Mesh's configured route minimum or the provider minimum), `amountExceedsMaximum` (above the provider's available liquidity; `message` carries the current maximum in USD), `permissionDenied`; schema violations return `invalidField` / `missingField`.

  **Create bridged address**: `POST /api/v1/bridging/address` (requires write access). Body: `userId` (required, max 384 chars, no PII) | `transactionId` (required, max 128 chars) | `origin` `{ network, asset }` (pay-in leg) | `destination` `{ network, asset, address }` (payout leg; `address` is the user's wallet on a withdrawal, your platform's receive address on a deposit). `userId` + `transactionId` identify the transfer (scoped to your client and environment); `origin`, `destination` and `address` are not part of the key but must match the in-progress record or you get `400 badRequest`. Once the transfer has succeeded, or the earlier address has expired and been cleaned up, the same `transactionId` mints a new address.

  **Create bridged address — response**: `content.tracking.transferId` (Mesh UUID, for status + webhooks) | `content.selectedRoute.bridgingAddress.address` (send `origin` asset on `origin` network here only) | `content.constraints` `{ minimumAmount, maximumAmount (always null today), expirationTime (unix secs, nullable), singleUseAddress }` | `content.warnings[]`. `minimumAmount` is a configured display hint (often `"0"`), not an enforced floor — Mesh does not reject an undersized pay-in and the bridge provider can still fail it at quote/fill time (notably BTC), so enforce it client-side. When a minimum is configured, `warnings` includes `Minimum transfer amount: {n} {ASSET}.`

  **Flow**: call route discovery with your native pairs → show native + bridged options → user picks. Native pair = skip the bridging APIs; handle it as you do today. Discovery does not know your native pairs, so filter overlapping routes client-side. Bridged pair = optionally call quote with the user's amount and show `estimatedAmount`, then call create-address, send pay-in to `bridgingAddress.address`, Mesh bridges to destination.

  **Critical**: addresses are single-use — new `transactionId` per pay-in, never reuse. Bridging is mainnet-only; test with small real amounts. Track via `transferId` + Transfer Status webhooks, or poll `GET /api/v1/transfers/managed/mesh` with `Id` = `tracking.transferId` (status can lag the on-chain fill by minutes; BTC can stay pending past the quote ETA without erroring).

  **Route discovery (withdrawal) — request body**:

  ```bash theme={null}
  # Replace YOUR_CLIENT_ID, YOUR_API_KEY and the sources entries.
  curl --request POST \
    --url https://integration-api.meshconnect.com/api/v1/bridging/routes/withdrawal \
    --header 'Content-Type: application/json' \
    --header 'X-Client-Id: YOUR_CLIENT_ID' \
    --header 'X-Client-Secret: YOUR_API_KEY' \
    --data '
  {
    "sources": [
      { "network": "eip155:137", "asset": "USDC" }
    ],
    "optimized": true
  }
  '
  ```

  **Create bridged address — request body**:

  ```bash theme={null}
  # Replace the credentials, userId, transactionId, origin and destination.
  curl --request POST \
    --url https://integration-api.meshconnect.com/api/v1/bridging/address \
    --header 'Content-Type: application/json' \
    --header 'X-Client-Id: YOUR_CLIENT_ID' \
    --header 'X-Client-Secret: YOUR_API_KEY' \
    --data '
  {
    "userId": "UNIQUE_USER_ID",
    "transactionId": "UNIQUE_TRANSACTION_ID",
    "origin": { "network": "eip155:137", "asset": "USDC" },
    "destination": {
      "network": "eip155:1",
      "asset": "USDC",
      "address": "0x1234567890123456789012345678901234567890"
    }
  }
  '
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.