Build an order
const url = 'https://api.hyperstream.dev/v2/build';const options = { method: 'POST', headers: {'X-API-Key': '<X-API-Key>', 'Content-Type': 'application/json'}, body: '{"srcToken":{"chainId":42161,"token":"0xaf88d065e77c8cC2239327C5EDb3A432268e5831"},"dstToken":{"chainId":8453,"token":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"},"amount":"100000000","intentOutcomeRate":"999700000000000000","terms":{"flatFee":"300000000000000000","expiresAt":"1787000000","signature":"0x60ce0a47bc64e07a5437de5fd4e6008bce46756d4d7c971710283924888b2a137a91ed09461b5848bb2b7341f5c258da3f97853e73662275f047e0c3c20882211b"},"depositor":"0x5b38da6a701c568545dcfcb03fcb875f56beddc4"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.hyperstream.dev/v2/build \ --header 'Content-Type: application/json' \ --header 'X-API-Key: <X-API-Key>' \ --data '{ "srcToken": { "chainId": 42161, "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" }, "dstToken": { "chainId": 8453, "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }, "amount": "100000000", "intentOutcomeRate": "999700000000000000", "terms": { "flatFee": "300000000000000000", "expiresAt": "1787000000", "signature": "0x60ce0a47bc64e07a5437de5fd4e6008bce46756d4d7c971710283924888b2a137a91ed09461b5848bb2b7341f5c258da3f97853e73662275f047e0c3c20882211b" }, "depositor": "0x5b38da6a701c568545dcfcb03fcb875f56beddc4" }'Turns a quote into an order and returns what the depositor’s wallet must send. Send quote.build unchanged, plus
depositor and, where required, payoutAddr and refundAddr. Contract-wallet depositors must pass both recipients
explicitly. Use a default only when the user controls that address on the relevant payout or refund chain.
- EVM source:
approveTxs(only if the allowance is short) thendepositTx, sent in order fromdepositor. - Non-EVM source (TRON): a
depositAddress; transfer exactlyexpectedAmountto it with enough time for source-chain confirmation and hub publication beforedeadline.
Not idempotent: every successful call creates a new order. Validation failures happen before order creation, but a 500
or 502 may leave a reserved TRON deposit address or an order record without tracking. An error is not a usable deposit
plan; do not fund a recovered order without confirming it with Khalani. Back off and retry at most once, then contact
Khalani with the x-request-id. Repeated retries can exhaust the deposit-address pool. If a call returned no response,
look for its order with GET /v2/orders?depositor=… before you build again. The order’s deadline
is orderTtl seconds from this call, not from the deposit. On every rail, the deadline is the intent’s publication expiry:
budget source-chain confirmation and hub publication. If insufficient time remains, rebuild before sending funds.
A deposit that cannot publish before expiry requires operator recovery.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”quote.build from /v2/quote, plus depositor and any recipients.
object
The token the depositor sends.
object
Spoke chain id.
Spoke-token contract address. TRON accepts base58 or hex41.
The token paid out.
object
Spoke chain id.
Spoke-token contract address. TRON accepts base58 or hex41.
The deposit, in source base units.
The rate the order publishes at, 1e18-scaled, from /v2/quote. Used as sent.
The quote’s attestation, echoed unchanged. Always send it: without it the rate is signed as sent and the flat fee is recomputed at current config. Expired returns 409; edited returns 400.
object
EVM address that signs and sends the deposit. Lowercase or exact EIP-55 checksum.
Receives the payout on the destination chain. Defaults to depositor; use the default only if the user controls that address on the destination chain. Contract-wallet depositors must pass an explicit recipient. Required when the destination is non-EVM (TRON).
Receives any refund on the source chain. Defaults to depositor; use the default only if the user controls that address on the source chain. Contract-wallet depositors must pass an explicit recipient. Required when the source is non-EVM (TRON).
Example
{ "srcToken": { "chainId": 42161, "token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" }, "dstToken": { "chainId": 8453, "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" }, "amount": "100000000", "intentOutcomeRate": "999700000000000000", "terms": { "flatFee": "300000000000000000", "expiresAt": "1787000000", "signature": "0x60ce0a47bc64e07a5437de5fd4e6008bce46756d4d7c971710283924888b2a137a91ed09461b5848bb2b7341f5c258da3f97853e73662275f047e0c3c20882211b" }, "depositor": "0x5b38da6a701c568545dcfcb03fcb875f56beddc4"}Responses
Section titled “Responses”The order is created in pending_deposit. Persist orderId.
Build result for an EVM source chain: the transactions for the depositor’s wallet to send.
object
keccak256(abi.encode(depositor, nonce)), lowercase. Keys every later call: persist it.
Decimal uint256 nonce of this deposit. Already part of orderId and the deposit.
Present only when the depositor’s allowance is too low. Send each in order and wait for it to confirm before depositTx. A leading approve(0) appears only when a residual allowance must be reset.
object
The source token contract.
Decimal wei; always 0.
Spoke chain id.
The deposit, unsigned. Send it exactly once, from depositor, after any approveTxs.
object
The source chain’s deposit contract.
Equals depositor. A deposit sent from any other account strands the funds.
Hex wei: the relay fee, sent as the transaction value.
Spoke chain id.
Hex gas limit; a fixed value, not an estimate.
Build result for a non-EVM source chain (TRON): no transactions, a deposit address instead.
object
keccak256(abi.encode(depositor, nonce)), lowercase. Keys every later call: persist it.
Decimal uint256 nonce of this deposit. Already part of orderId and the deposit.
Transfer the source token here, from any wallet.
The exact amount to transfer, in source base units. A transfer of any other amount is rejected.
Intent publication expiry, in unix seconds. Budget time for source-chain confirmation and hub publication before this deadline. If insufficient time remains, rebuild before sending funds; a deposit that cannot publish before expiry requires operator recovery.
Example
{ "orderId": "0x35dc8d390877c71baacadb4001eea0055804cfc306f599fbfc97c1d69d64d30a", "nonce": "48210338951092635551940726350418473090118563497023168275518271946413510239861", "approveTxs": [ { "to": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "data": "0x095ea7b3000000000000000000000000b280c373f57c9169e009a66496ee556084afad240000000000000000000000000000000000000000000000000000000005f5e100", "value": "0", "chainId": 42161 } ], "depositTx": { "to": "0xb280C373F57c9169e009a66496EE556084afAd24", "from": "0x5b38da6a701c568545dcfcb03fcb875f56beddc4", "data": "0xf3125a1f000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e58310000000000000000000000000000000000000000000000000000000005f5e100", "value": "0x5af3107a4000", "chainId": 42161, "gasLimit": "0x3d090" }}Invalid body (ValidationError), malformed JSON (NamedError), or a rejected request (Error): an unsupported token, an amount at or below the flat fee, a request edited after its quote, or a missing or invalid recipient.
The request failed schema validation. Carries message, not error.
object
object
Maintenance, a malformed JSON body, or an unhandled failure. Carries message, not error.
object
MaintenanceException (503), BadRequestException (malformed JSON) or InternalErrorException (500).
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "Quoted terms do not match the signature — re-quote"}The X-API-Key header is missing.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "X-API-Key required"}The key is unknown or past its rotation window, or your integrator registry row is disabled or invalid. A 403 is always about your account: do not retry.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "Unknown or inactive integrator"}Source and destination are the same asset.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "Source and destination resolve to the same mToken"}The quote’s attestation is older than five minutes. Re-quote; nothing was stored.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "Quoted terms expired at 1787000000"}Rate limited per integrator and per IP. No Retry-After; back off and retry.
Rate limited. No Retry-After; back off and retry.
object
Example
{ "errorCode": "RATE_LIMITED", "message": "Too many requests, please retry later"}Server-side failure (Error) or an unhandled one (NamedError). Retry with a new build, then escalate.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Maintenance, a malformed JSON body, or an unhandled failure. Carries message, not error.
object
MaintenanceException (503), BadRequestException (malformed JSON) or InternalErrorException (500).
Example
{ "error": "Failed to persist pending order"}This deployment cannot take deposits from the source chain. detail.missing names the missing setting.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example generated
{ "error": "example", "detail": { "minSrcAmount": "example", "maxSrcAmount": "example", "maxOutcomeAmount": "example", "missing": "example" }}A chain read or dependent service failed. A deposit-address reservation or order record may remain. Do not fund a recovered order without confirming it with Khalani; back off and retry at most once, then contact Khalani with the x-request-id.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Example
{ "error": "Failed to quote deposit relay fee"}Temporarily unavailable: your terms could not be read, a price or fee configuration is missing, or no solver can serve the direction (Error), or the API is in maintenance (NamedError with Retry-After). The variants cannot be told apart reliably, so cap your retries.
An error raised by the route itself.
object
object
Smallest deposit that would quote, in source base units.
Largest deposit that would quote, in source base units.
Largest output that would quote, in destination base units.
The deployment setting this direction needs.
Maintenance, a malformed JSON body, or an unhandled failure. Carries message, not error.
object
MaintenanceException (503), BadRequestException (malformed JSON) or InternalErrorException (500).
Example
{ "name": "MaintenanceException", "message": "SystemMaintenance"}Headers
Section titled “Headers”Seconds to wait. Sent only by the maintenance response.