Before you start
- Your core Mesh deposit integration is working end-to-end (see Fetch a 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)
Overview
In a standard deposit integration, every entry intoAddresses 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 anaddress 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.
Recommended: pass the addresses you already have, plus the most common ecosystemsJIT 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
How it works
- You request a Link Token with your full
toAddressesarray, omittingaddressfor any pair you want Mesh to fetch on demand. - The user selects a token and network in Link.
- If that entry has no
address, Mesh calls your initiate endpoint with theuserId,networkId, andsymbol. - You respond immediately:
202 Acceptedif generation has started, or200 OKwith the address if it’s already available. - Mesh polls your status endpoint about every 2–3 seconds until the address is
readyorfailed, or until the overall deadline you set on the JIT Config page (default about 3 minutes) passes. - 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). 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).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 below) and Mesh skips polling.200 OKwith{ "status": "failed", "reason": "..." }: generation can’t start. Mesh ends the attempt.- Any other status code is treated as a failed generation.
202 needs no body and no signature; Mesh doesn’t read it.
Parameter reference
Parameter reference
userIdThe end-user identifier you passed in the Link Token request.
networkIdMesh’s network UUID for the blockchain network the user selected.
symbolThe token ticker the user selected (eg.
USDC, ETH, XRP).transactionIdThe
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.Request from Mesh
Request from Mesh
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.
Request from Mesh
Request from Mesh
Responses
Responses
Still generating:Ready (also the body for a Failed:
200 OK on initiate):Ready response
Parameter reference
Parameter reference
status requiredOne of
ready, pending, or failed.userId requiredMust exactly match the request.
networkId requiredMust exactly match the request.
symbol requiredMust exactly match the request.
address requiredA valid deposit address for the given network and token.
addressTag optional required for memo-chain networksThe destination tag or memo for memo/tag networks (see Include tags for memo-chain networks). Omit it otherwise.
reason optionalA human-readable failure reason when
status is failed.Keep addresses idempotent
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 includeaddressTag in the ready response for these networks.
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:
To verify a request:
- 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}. - Compute
hex(HMAC-SHA256(shared_secret, signature_input))and compare it toX-Mesh-Signature. - Reject the request (any non-
200response) if the signature doesn’t match, or ifX-Mesh-Timestampis more than 60 seconds from your current time.
{METHOD}: uppercase, such asPOSTorGET.{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 (onlyA-Z a-z 0-9 - . _ ~stay unencoded; a space becomes%20) and join them ask=vpairs 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 every200 response:
X-Mesh-Response-Timestamp: the current Unix timestamp in secondsX-Mesh-Signature:hex(HMAC-SHA256(shared_secret, "jit-response:v1\n" + sha256(response_body) + "\n" + X-Mesh-Response-Timestamp))
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.
Node.js (Express) example
Node.js (Express) example
- 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
rfc3986does. PlainencodeURIComponentleaves! ' ( ) *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-Noncefor 60 seconds and reject duplicates.
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.
Update your Link Token request
Once your JIT config is saved, omitaddress from any toAddresses entry you want Mesh to fetch on demand. Entries that include address work exactly as they do today.
Link Token request
Link Token request
- JIT only triggers for entries with no
address. If you keep sendingaddressvalues, nothing changes. - If you omit
addresswithout a saved JIT config, the Link Token request fails with400. JIT works fordeposittransfers only. - Use the Get networks endpoint (/api/v1/transfers/managed/networks) to find each
networkId. These values don’t change, so you can cache them.
Want help building your array? Use the toAddress generator to construct your
toAddresses quickly.Performance and reliability requirements
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.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.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.
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
toAddressesentry that has noaddress, select it in Link, and confirm Mesh receives202, polls, and shows the address once ready. - Inline path: Select a pair whose address already exists and confirm your initiate endpoint returns
200with 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: failedand confirm Link returns the user to token/network selection.
What’s next
Next up: Transfer status webhooks, track every deposit, including those sent to JIT addresses, as it moves from pending to succeeded.AI coding reference (llms.txt)
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). Human readers can safely ignore this.llms.txt — Just-in-Time Address FetchingJIT 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