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

# Just-in-time address fetching

By the end of this guide, you'll have two endpoints that let Mesh fetch deposit addresses on demand during a Link session, so you can offer every token and network you support without pre-generating an address for each one.

<Check>
  **Before you start**

  * Your core Mesh deposit integration is working end-to-end (see [Fetch a Link Token](/build/fetch-link-token))
  * Your platform can generate a deposit address for a given user, network, and token through your own API or custodian
  * Your Mesh representative has enabled the **JIT Config** page in your Mesh dashboard. JIT goes live as soon as you save both endpoint URLs and a signing secret there (see [Build your JIT endpoints](#build-your-jit-endpoints))
</Check>

## Overview

In a standard deposit integration, every entry in `toAddresses` in the Link Token request includes an `address`. That means you need an address ready for every token/network pair before the session starts, which pushes many platforms to offer only a handful of options.

With **Just-in-Time (JIT) address fetching**, you can leave `address` out of any `toAddresses` entry. When the user selects that token and network in Link, Mesh calls your endpoints to get the address on demand. Because address generation can take time on some networks (sometimes over a minute), Mesh uses an **initiate-then-poll** model: Mesh asks you to start generating, then polls until the address is ready.

## Benefits

### Offer every option you support

Pass the full array of tokens and networks you support in every Link session, without pre-generating an address for each one. **Mesh highly recommends this:** the more options a user has, the more likely they are to find the asset and network they hold, and the higher the chance they complete their deposit.

### Keep the most common paths instant

JIT and pre-generated addresses work together in the same request. Entries that include an `address` work exactly as they do today; Mesh only calls your endpoints for entries without one. Users never wait on generation for the options you've already supplied.

<Info>
  **Recommended: pass the addresses you already have, plus the most common ecosystems**

  JIT adds some latency the first time a user selects a token/network pair. To keep the experience fast for most users, include an `address` for:

  * Any address you've **already generated for this user**
  * The **most commonly used ecosystems**, such as Bitcoin, EVM networks, Solana, and XRPL

  Then rely on JIT for the long tail.
</Info>

## How it works

1. You request a Link Token with your full `toAddresses` array, omitting `address` for any pair you want Mesh to fetch on demand.
2. The user selects a token and network in Link.
3. If that entry has no `address`, Mesh calls your **initiate** endpoint with the `userId`, `networkId`, and `symbol`.
4. You respond immediately: `202 Accepted` if generation has started, or `200 OK` with the address if it's already available.
5. Mesh polls your **status** endpoint about every 2–3 seconds until the address is `ready` or `failed`, or until the overall deadline you set on the JIT Config page (default about 3 minutes) passes.
6. Mesh verifies your response signature and validates the address, then shows it to the user and continues the deposit.

## Build your JIT endpoints

You expose two HTTPS endpoints. Both sit in the **live deposit path**, so treat them as production-critical infrastructure.

Once they're built, register both URLs, your signing secret, and your overall generation deadline in the Mesh dashboard under **Company > JIT Config** ([dashboard.meshconnect.com/company/jitConfig](https://dashboard.meshconnect.com/company/jitConfig)). This config is for production only. Sandbox never calls your endpoints, so there's no sandbox URL or secret to set (see [Test your implementation](#test-your-implementation)).

### 1. Initiate endpoint (`POST`)

Mesh calls this endpoint when a user selects a token/network pair with no `address`. Start generating the address and **respond immediately**; don't hold the request open while generation runs.

* **`202 Accepted`**: generation has started. Mesh begins polling your status endpoint.
* **`200 OK`**: the address is already available. Return the full ready payload (see [Ready response](#ready-response) below) and Mesh skips polling.
* **`200 OK`** with `{ "status": "failed", "reason": "..." }`: generation can't start. Mesh ends the attempt.
* **Any other status code** is treated as a failed generation.

A `202` needs no body and no signature; Mesh doesn't read it.

<div className="parameter-reference">
  <Accordion title="Parameter reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>userId</code></div>
        <div className="param-body">The end-user identifier you passed in the Link Token request.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>networkId</code></div>
        <div className="param-body">Mesh's network UUID for the blockchain network the user selected.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>symbol</code></div>
        <div className="param-body">The token ticker the user selected (eg. `USDC`, `ETH`, `XRP`).</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>transactionId</code></div>
        <div className="param-body">The `transactionId` you passed in `transferOptions` in the Link Token request, or an empty string if you didn't pass one. You can optionally check that it maps to the given `userId` and reject stale requests, but sessions are valid for 1 hour, so **don't reject a `transactionId` that's less than 1 hour old**. Address generation and idempotency are always keyed on the combination of `userId`, `networkId`, and `symbol`, never on `transactionId`.</div>
      </div>
    </div>
  </Accordion>
</div>

<Accordion title="Request from Mesh">
  ```bash theme={null}
  POST {your_initiate_url}
  Content-Type: application/json
  X-Mesh-Timestamp: 1791230400
  X-Mesh-Nonce: 6f1c2b7e-3a4d-4c8e-9f21-0b5d7e8a9c13
  X-Mesh-Signature: 9b2f...e41a

  {
    "userId": "UNIQUE_USER_ID",
    "networkId": "0ea47ee7-9d36-460e-a2d5-64cfa8a1dddd",
    "symbol": "XRP",
    "transactionId": "UNIQUE_TRANSACTION_ID"
  }
  ```
</Accordion>

### 2. Status endpoint (`GET`)

Mesh polls this endpoint with the same `userId`, `networkId`, and `symbol` it sent to initiate, plus `transactionId`. Always return `200 OK` with one of three statuses: `pending`, `ready`, or `failed`. A non-`2xx` status, a call over 5s, or a connection failure on any single poll ends the attempt; Mesh doesn't keep polling after it.

<AccordionGroup>
  <Accordion title="Request from Mesh">
    ```bash theme={null}
    GET {your_status_url}?userId=UNIQUE_USER_ID&networkId=0ea47ee7-9d36-460e-a2d5-64cfa8a1dddd&symbol=XRP&transactionId=UNIQUE_TRANSACTION_ID
    X-Mesh-Timestamp: 1791230403
    X-Mesh-Nonce: 0c9a4e2d-7b13-4f5a-8e66-2d1f9b3c7a40
    X-Mesh-Signature: 4d7a...b902
    ```
  </Accordion>

  <Accordion title="Responses">
    **Still generating:**

    ```json theme={null}
    { "status": "pending" }
    ```

    **Ready** (also the body for a `200 OK` on initiate):

    ```json theme={null}
    {
      "status": "ready",
      "userId": "UNIQUE_USER_ID",
      "networkId": "0ea47ee7-9d36-460e-a2d5-64cfa8a1dddd",
      "symbol": "XRP",
      "address": "rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY",
      "addressTag": "38291045"
    }
    ```

    **Failed:**

    ```json theme={null}
    { "status": "failed", "reason": "Network temporarily unavailable at custodian" }
    ```
  </Accordion>
</AccordionGroup>

### Ready response

<div className="parameter-reference">
  <Accordion title="Parameter reference">
    <div className="param-list">
      <div className="param-row">
        <div className="param-name"><code>status</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">One of `ready`, `pending`, or `failed`.</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">Must exactly match the request.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>networkId</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Must exactly match the request.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>symbol</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">Must exactly match the request.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>address</code> <span className="param-tag param-tag-required">required</span></div>
        <div className="param-body">A valid deposit address for the given network and token.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>addressTag</code> <span className="param-tag param-tag-optional">optional</span> <span className="param-tag param-tag-required">required for memo-chain networks</span></div>
        <div className="param-body">The destination tag or memo for memo/tag networks (see [Include tags for memo-chain networks](#include-tags-for-memo-chain-networks)). Omit it otherwise.</div>
      </div>

      <div className="param-row">
        <div className="param-name"><code>reason</code> <span className="param-tag param-tag-optional">optional</span></div>
        <div className="param-body">A human-readable failure reason when `status` is `failed`.</div>
      </div>
    </div>
  </Accordion>
</div>

<Warning>
  **Echo back the exact `userId`, `networkId`, and `symbol`.** Mesh rejects any ready response where these don't match the request, and the deposit can't continue on that token/network.
</Warning>

## Keep addresses idempotent

<Warning>
  **This is critical. Violations can result in lost funds.**

  For a given `userId` + `networkId` + `symbol`, your endpoints must **always return the same address and tag**. Repeated initiate calls must never mint a new address, and a status poll must never trigger generation. If they do, the user may see one address while the transfer executes against another, sending funds to an address no one can access.
</Warning>

**Recommended pattern:**

* On the **first** initiate for a `userId` + `networkId` + `symbol`, start generation and persist the result against that user.
* On every later initiate or status call for the same combination, return the stored value.
* Within a single Link session, Mesh keeps the first address it receives so every step of that session uses the same one. Mesh doesn't reuse addresses across sessions: a new session calls initiate again, so **your endpoint is the source of truth**. To skip JIT for an address you've already generated, pass it in the next Link Token request.

## Include tags for memo-chain networks

Many networks route deposits to the right account using a destination tag or memo, including XRP, XLM, Hedera, EOS, WAX, Casper, Mina, Injective, and Cosmos-based networks (such as Cosmos Hub, Osmosis, dYdX, Celestia, Crypto.org, and Kava). Always include `addressTag` in the ready response for these networks.

<Warning>
  **A missing `addressTag` on a memo chain is rejected.** Without a tag, a deposit can land in your omnibus address with no way to attribute it to the user, so Mesh treats a missing tag on these networks as a failure.
</Warning>

## Authenticate requests and responses

Mesh signs every request with **HMAC-SHA256** using a single shared secret, and you sign every response with the same secret. Each direction uses its own prefix (`jit-request:v1` and `jit-response:v1`), so a request signature can never be reused as a response signature.

### Verify Mesh's requests

Mesh sends three headers on every call to both endpoints:

| Header | Description |
| - | - |
| **`X-Mesh-Timestamp`** | Unix timestamp (seconds) when Mesh sent the request. |
| **`X-Mesh-Nonce`** | A unique UUID per request. |
| **`X-Mesh-Signature`** | Hex-encoded HMAC-SHA256 signature of the request. |

To verify a request:

1. Build the signature input by joining these parts with a newline (`\n`): `jit-request:v1`, `{METHOD}`, `{path}`, `{sorted-query-string}`, `{sha256-of-body}`, `{X-Mesh-Timestamp}`, `{X-Mesh-Nonce}`.
2. Compute `hex(HMAC-SHA256(shared_secret, signature_input))` and compare it to `X-Mesh-Signature`.
3. Reject the request (any non-`200` response) if the signature doesn't match, or if `X-Mesh-Timestamp` is more than **60 seconds** from your current time.

**Canonicalization rules** (you must produce these exact bytes):

* `{METHOD}`: uppercase, such as `POST` or `GET`.
* `{path}`: the path of the URL you registered, verbatim, including any base path. If a proxy or gateway rewrites the path before it reaches your code, verify against the registered path.
* `{sorted-query-string}`: decode each query key and value, sort the pairs by key and then by value in plain byte (ordinal) order, then percent-encode each key and value per RFC 3986 (only `A-Z a-z 0-9 - . _ ~` stay unencoded; a space becomes `%20`) and join them as `k=v` pairs with `&`. Empty for initiate, which has no query.
* `{sha256-of-body}`: lowercase hex SHA-256 of the raw body bytes. Status polls have no body, so they hash the empty string: `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`.
* `{X-Mesh-Timestamp}`: Unix seconds as a base-10 integer.

### Sign your responses

Add two headers to every `200` response:

* `X-Mesh-Response-Timestamp`: the current Unix timestamp in seconds
* `X-Mesh-Signature`: `hex(HMAC-SHA256(shared_secret, "jit-response:v1\n" + sha256(response_body) + "\n" + X-Mesh-Response-Timestamp))`

Mesh rejects any response with a missing or invalid signature and doesn't use the address. Mesh also rejects a response whose `X-Mesh-Response-Timestamp` is more than 60 seconds from its own clock, so keep your servers synced with NTP. You don't need to sign a `202`.

<Accordion title="Node.js (Express) example">
  ```javascript theme={null}
  const crypto = require('crypto')
  const express = require('express')

  const app = express()
  const SECRET = process.env.MESH_JIT_SECRET // Replace: the secret you saved on the JIT Config page

  const sha256Hex = (data) => crypto.createHash('sha256').update(data).digest('hex')
  const hmacHex = (input) => crypto.createHmac('sha256', SECRET).update(input).digest('hex')
  // RFC 3986: encodeURIComponent leaves ! ' ( ) * unencoded, Mesh doesn't
  const rfc3986 = (s) => encodeURIComponent(s).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase())
  const ordinal = (a, b) => (a < b ? -1 : a > b ? 1 : 0)

  function verifyMeshRequest(req, rawBody) {
    const ts = req.get('X-Mesh-Timestamp')
    const nonce = req.get('X-Mesh-Nonce')
    const sig = req.get('X-Mesh-Signature')
    if (!ts || !nonce || !sig) return false

    // Replay protection: reject anything older or newer than 60 seconds
    if (Math.abs(Math.floor(Date.now() / 1000) - Number(ts)) > 60) return false

    const url = new URL(req.originalUrl, 'https://placeholder')
    const sortedQuery = [...url.searchParams.entries()]
      .sort(([ka, va], [kb, vb]) => ordinal(ka, kb) || ordinal(va, vb))
      .map(([k, v]) => `${rfc3986(k)}=${rfc3986(v)}`)
      .join('&')

    const input = [
      'jit-request:v1',
      req.method,
      url.pathname,
      sortedQuery,
      sha256Hex(rawBody),
      ts,
      nonce,
    ].join('\n')

    const expected = Buffer.from(hmacHex(input))
    const received = Buffer.from(sig)
    return expected.length === received.length && crypto.timingSafeEqual(expected, received)
  }

  function sendSigned(res, httpStatus, body) {
    const json = JSON.stringify(body)
    const ts = Math.floor(Date.now() / 1000).toString()
    const sig = hmacHex(`jit-response:v1\n${sha256Hex(json)}\n${ts}`)
    res
      .status(httpStatus)
      .set({
        'Content-Type': 'application/json',
        'X-Mesh-Response-Timestamp': ts,
        'X-Mesh-Signature': sig,
      })
      .send(json)
  }

  function readyBody(userId, networkId, symbol, record) {
    return {
      status: 'ready',
      userId, // Must echo the request exactly
      networkId,
      symbol,
      address: record.address,
      ...(record.addressTag && { addressTag: record.addressTag }), // Required for memo chains
    }
  }

  // Initiate: respond immediately, never mint a second address
  app.post('/mesh/jit/initiate', express.raw({ type: '*/*' }), async (req, res) => {
    if (!verifyMeshRequest(req, req.body)) return res.sendStatus(401)
    const { userId, networkId, symbol } = JSON.parse(req.body.toString())

    const record = await addressStore.get(userId, networkId, symbol) // Replace with your storage
    if (record?.address) return sendSigned(res, 200, readyBody(userId, networkId, symbol, record))

    await addressStore.startGenerationOnce(userId, networkId, symbol) // Replace: idempotent per triple
    return res.sendStatus(202) // No body or signature needed
  })

  // Status: read only, never triggers generation
  app.get('/mesh/jit/status', async (req, res) => {
    if (!verifyMeshRequest(req, Buffer.alloc(0))) return res.sendStatus(401)
    const { userId, networkId, symbol } = req.query

    const record = await addressStore.get(userId, networkId, symbol) // Replace with your storage
    if (record?.address) return sendSigned(res, 200, readyBody(userId, networkId, symbol, record))
    if (record?.failed) return sendSigned(res, 200, { status: 'failed', reason: record.reason })
    return sendSigned(res, 200, { status: 'pending' })
  })
  ```

  **Notes:**

  * Use the **raw** request body for the body hash. Parsing and re-serializing JSON changes the bytes and breaks verification.
  * Percent-encode query keys and values the way `rfc3986` does. Plain `encodeURIComponent` leaves `! ' ( ) *` unencoded and breaks verification for IDs containing them.
  * Compare signatures with a constant-time function like `crypto.timingSafeEqual`.
  * For extra replay protection, cache each `X-Mesh-Nonce` for 60 seconds and reject duplicates.
</Accordion>

### Rotate your secret

Rotation is self-service in the Mesh dashboard. The signing secret is **write-only**: you can enter a new value at any time, and Mesh stores it encrypted and never shows it again.

* **Planned rotation:** as soon as you save a new secret, Mesh signs every request with it. Mesh keeps accepting **response** signatures made with either the old or new secret for an overlap window (**default 24 hours**), then retires the old one. So update your endpoints **before** you save the new secret: verify requests against both the old and new secret, and keep signing responses with the old one. After you save, switch response signing to the new secret and drop the old one before the window ends.
* **Suspected compromise:** save a new secret, then disable the previous secret on the JIT Config page to retire it immediately instead of waiting for the overlap window. Requests signed with the retired secret fail into the standard error path, with no risk of lost funds.

<Warning>
  **Keep your shared secret confidential.** Store it in a secrets manager, never in client-side code or source control.
</Warning>

## Update your Link Token request

Once your JIT config is saved, omit `address` from any `toAddresses` entry you want Mesh to fetch on demand. Entries that include `address` work exactly as they do today.

<Accordion title="Link Token request">
  ```javascript theme={null}
  curl --request POST \
    --url https://integration-api.meshconnect.com/api/v1/linktoken \
    --header 'Content-Type: application/json' \
    --header 'X-Client-Id: YOUR_CLIENT_ID' \ // Replace
    --header 'X-Client-Secret: YOUR_API_KEY' \ // Replace
    --data '
  {
    "userId": "UNIQUE_USER_ID", // Replace
    "transferOptions": {
      "transactionId": "UNIQUE_TRANSACTION_ID", // Replace
      "transferType": "deposit",
      "toAddresses": [
        // Pre-generated: the most common ecosystems
        {
          "networkId": "03dee5da-7398-428f-9ec2-ab41bcb271da", // Bitcoin
          "symbol": "BTC",
          "address": "xxx" // Replace
        },
        {
          "networkId": "e3c7fdd8-b1fc-4e51-85ae-bb276e075611", // Ethereum
          "symbol": "USDC",
          "address": "xxx" // Replace
        },
        {
          "networkId": "aa883b03-120d-477c-a588-37c2afd3ca71", // Base
          "symbol": "USDC",
          "address": "xxx" // Replace
        },
        {
          "networkId": "0291810a-5947-424d-9a59-e88bb33e999d", // Solana
          "symbol": "USDC",
          "address": "xxx" // Replace
        },
        {
          "networkId": "0ea47ee7-9d36-460e-a2d5-64cfa8a1dddd", // XRP Ledger
          "symbol": "XRP",
          "address": "xxx", // Replace
          "addressTag": "xxx" // Replace: destination tag
        },
        // JIT: no address, fetched from your endpoints only if the user selects it
        {
          "networkId": "MESH_NETWORK_ID", // Replace with a long-tail network
          "symbol": "USDT" // Replace
        },
        {
          "networkId": "MESH_NETWORK_ID", // Replace with a long-tail network
          "symbol": "USDC" // Replace
        }
      ]
    }
  }
  '
  ```

  **Notes:**

  * JIT only triggers for entries with no `address`. If you keep sending `address` values, nothing changes.
  * If you omit `address` without a saved JIT config, the Link Token request fails with `400`. JIT works for `deposit` transfers only.
  * Use the **Get networks** endpoint ([/api/v1/transfers/managed/networks](/api-reference/managed-transfers/get-networks)) to find each `networkId`. These values don't change, so you can cache them.
</Accordion>

<Info>
  **Want help building your array?** Use the [toAddress generator](https://mesh-address-generator.vercel.app/?section=link) to construct your `toAddresses` quickly.
</Info>

## Performance and reliability requirements

| Requirement | Target |
| - | - |
| **Per-call response time** | 2s or less at p99. **5s hard timeout**: any initiate or status call over 5s is treated as a timeout. |
| **Total generation time** | Ready within the overall generation deadline you set on the JIT Config page (default about 3 minutes). Otherwise the user is returned to token/network selection. |
| **Uptime** | 99.9%+ recommended. These endpoints are in the live deposit path. |
| **Monitoring** | Alert on failures. Every failure stops the deposit for that token/network; there's no fallback address. |
| **Infrastructure** | Redundant deployment recommended. |

## Handle errors

While generation is pending, the user sees a "Generating your deposit address" screen and can go back or exit at any time. If the attempt fails or the deadline passes, Mesh stops and returns the user to the previous screen, where they can choose the same or a different token and network. Mesh polls your status endpoint while generation is pending, but **never calls initiate again on its own after a failure**.

| Your response | What Mesh does |
| - | - |
| `202`, then `status: ready` (signed and valid) | Polls, then continues. Seamless for the user. |
| `200` with `status: ready` inline (signed and valid) | Continues immediately. Seamless for the user. |
| Missing or invalid response signature | Rejects the address. |
| Ready response missing `addressTag` on a memo chain | Rejects the address to prevent lost funds. |
| Address fails network format validation | Rejects and logs the address. |
| Echoed `userId`, `networkId`, or `symbol` doesn't match | Rejects the address. |
| `status: failed` | Treats the request as failed. |
| Still `pending` after the overall deadline | Stops polling and treats the request as failed. |
| Any initiate or status call over 5s, or endpoint unreachable | Ends the attempt. Mesh doesn't keep polling after a failed call. |
| Initiate returns a status other than `200` or `202` | Treats the request as failed. |
| A status poll returns a non-`2xx` status | Treats the request as failed. |
| You reject the request signature (non-`200`) | Treats the request as failed. |

## Allowlist Mesh's IP addresses

Before production go-live, Mesh provides the fixed outbound IP address or range for the servers that call your JIT endpoints. You can allowlist it at your network layer to restrict the endpoints to Mesh traffic.

<Info>
  **Allowlist a CIDR range, not a single IP.** Mesh may add outbound IPs as it scales, and a range avoids an allowlist change each time. Mesh confirms the exact range in writing before go-live.
</Info>

## Test your implementation

**Sandbox doesn't call your JIT endpoints.** For address-less entries, sandbox fills in a placeholder address instead, and it drops address-less entries on networks with no placeholder, including all memo-chain networks. Use sandbox to check that your Link Token requests with address-less entries work end to end.

**Test your endpoints in production behind a gate.** Mesh recommends testing the full JIT flow end to end in production, with JIT entries gated to a group of your own test users and Mesh test users, before rolling out to everyone. If you can't gate by user, Mesh can configure additional API keys for testing; ask your Mesh representative.

**What to test:**

* **Async path**: Launch a session with a `toAddresses` entry that has no `address`, select it in Link, and confirm Mesh receives `202`, polls, and shows the address once ready.
* **Inline path**: Select a pair whose address already exists and confirm your initiate endpoint returns `200` with the ready payload and Link shows it immediately.
* **Idempotency**: Select the same pair twice (or close and reopen Link) and confirm the same address and tag come back every time.
* **Memo chain**: Select XRP or XLM and confirm the ready response includes `addressTag`.
* **Signatures**: Send a request with a bad signature or a timestamp over 60 seconds old and confirm your endpoint rejects it.
* **Failure path**: Return `status: failed` and confirm Link returns the user to token/network selection.

## What's next

Next up: [Transfer status webhooks](/resources/webhooks), track every deposit, including those sent to JIT addresses, as it moves from pending to succeeded.

***

<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 — Just-in-Time Address Fetching**

  JIT lets a client omit `address` from `transferOptions.toAddresses[]` entries in the Link Token request. When the user selects that token/network, Mesh calls two client-hosted HTTPS endpoints to get the address on demand (initiate-then-poll). Mesh must first enable the JIT Config page for the client; JIT is live once the initiate URL, status URL, and signing secret are saved there. Production only. Recommended: pass the full array of supported pairs; include `address` for any pre-generated addresses and for the most common ecosystems (Bitcoin, EVM, Solana, XRPL); use JIT for the long tail.

  **Endpoint 1 — Initiate:** `POST {your_initiate_url}`. Body: `userId` | `networkId` (Mesh network UUID) | `symbol` | `transactionId` (your Link Token `transferOptions.transactionId`, empty string if none; optional to validate; never reject before 1 hour old). Respond immediately: `202` (generation started; no body or signature needed) | `200` with the ready payload (already available; Mesh skips polling) | `200` with `{ "status": "failed", reason }` (can't start; attempt ends). Any other status code = failed generation.

  **Endpoint 2 — Status:** `GET {your_status_url}?userId=&networkId=&symbol=&transactionId=`. Always `200` with `{ "status": "pending" }`, `{ "status": "ready", userId, networkId, symbol, address, addressTag? }`, or `{ "status": "failed", reason }`. Mesh polls every 2–3s until ready/failed or the overall deadline set on the JIT Config page (default about 3 min). A non-2xx, a call over 5s, or a connection failure on any single poll ends the attempt.

  **Ready payload rules:** echo `userId`, `networkId`, `symbol` exactly (mismatch is rejected) | `address` required and must pass network format validation | `addressTag` required for memo/tag chains (XRP, XLM, Hedera, EOS, WAX, Casper, Mina, Injective, and Cosmos-based networks such as Cosmos Hub, Osmosis, dYdX, Celestia, Crypto.org, Kava), omit otherwise.

  **Idempotency (critical, funds-loss risk):** for a given `userId` + `networkId` + `symbol`, always return the same address and tag. Repeat initiates must not mint a new address; status must never trigger generation. Persist on first initiate. Mesh keeps the address only within one Link session; a new session re-initiates. Pass already-generated addresses in the Link Token.

  **Inbound auth (both endpoints):** headers `X-Mesh-Timestamp` (unix seconds) | `X-Mesh-Nonce` (UUID) | `X-Mesh-Signature`. Signature input = `jit-request:v1\n{METHOD}\n{path}\n{sorted-query-string}\n{sha256-of-body}\n{X-Mesh-Timestamp}\n{X-Mesh-Nonce}`; compare `hex(HMAC-SHA256(shared_secret, input))`. Reject (non-200) on mismatch or timestamp skew over 60s. Canonicalization: method uppercase; path verbatim from the registered URL; query pairs sorted ordinally by decoded key then value, then RFC 3986 percent-encoded; body hash lowercase hex SHA-256 (empty string for GET); timestamp in Unix seconds.

  **Outbound auth (every response):** headers `X-Mesh-Response-Timestamp` (unix seconds) and `X-Mesh-Signature` = `hex(HMAC-SHA256(shared_secret, "jit-response:v1\n" + sha256(response_body) + "\n" + X-Mesh-Response-Timestamp))`. Unsigned or invalid 200 responses are rejected, as are response timestamps over 60s off. A 202 needs no body or signature.

  **Secret rotation:** self-service in the Mesh dashboard; write-only field. Mesh signs requests with the new secret immediately and accepts old-or-new response signatures for 24 hours (default), so accept the new secret on your side before saving it. For a compromise, save a new secret, then disable the previous one on the JIT Config page to retire it immediately.

  **Performance:** p99 ≤ 2s per call; 5s hard timeout per call; 99.9%+ uptime recommended; alert on failures.

  **Errors:** any failed call, `failed` status, initiate code other than 200/202, or non-2xx poll ends the attempt and returns the user to token/network selection. Mesh polls while pending but never re-initiates after a failure.

  **Endpoint registration:** register initiate and status URLs, the signing secret, and the overall generation deadline in the Mesh dashboard at `dashboard.meshconnect.com/company/jitConfig`.

  **Network access:** Mesh provides a fixed outbound IP/CIDR range before go-live; allowlist the range.

  **Testing:** sandbox never calls JIT endpoints (placeholder addresses; memo-chain address-less entries dropped). Test end to end in production, gated to client and Mesh test users, or with additional API keys Mesh configures.

  **Link Token:** `POST /api/v1/linktoken` with `transferOptions.transferType: "deposit"` and `toAddresses[]` entries of `networkId` + `symbol` (+ `address`, + `addressTag` for memo chains). Entries without `address` use JIT. Omitting `address` without a saved JIT config returns `400`. JIT works for `deposit` only. Get networkIds from `GET /api/v1/transfers/managed/networks` (stable; cache them).

  *For the full consolidated reference across all guides, see [docs.meshconnect.com/llms-full.txt](https://docs.meshconnect.com/llms-full.txt)*
</Accordion>


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