Skip to content
API reference

Build an order

POST
/v2/build
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) then depositTx, sent in order from depositor.
  • Non-EVM source (TRON): a depositAddress; transfer exactly expectedAmount to it with enough time for source-chain confirmation and hub publication before deadline.

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.

Media typeapplication/json

quote.build from /v2/quote, plus depositor and any recipients.

object
srcToken
required

The token the depositor sends.

object
chainId
required

Spoke chain id.

integer
token
required

Spoke-token contract address. TRON accepts base58 or hex41.

string
dstToken
required

The token paid out.

object
chainId
required

Spoke chain id.

integer
token
required

Spoke-token contract address. TRON accepts base58 or hex41.

string
amount
required

The deposit, in source base units.

string
/^[1-9]\d*$/
intentOutcomeRate
required

The rate the order publishes at, 1e18-scaled, from /v2/quote. Used as sent.

string
/^[1-9]\d*$/
terms

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
flatFee
required
string
/^\d+$/
expiresAt
required
string
/^\d+$/
signature
required
string
/^0x[0-9a-fA-F]+$/
depositor
required

EVM address that signs and sends the deposit. Lowercase or exact EIP-55 checksum.

string
/^0x[0-9a-fA-F]{40}$/
payoutAddr

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

string
refundAddr

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

string

Example

{
"srcToken": {
"chainId": 42161,
"token": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
},
"dstToken": {
"chainId": 8453,
"token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
},
"amount": "100000000",
"intentOutcomeRate": "999700000000000000",
"terms": {
"flatFee": "300000000000000000",
"expiresAt": "1787000000",
"signature": "0x60ce0a47bc64e07a5437de5fd4e6008bce46756d4d7c971710283924888b2a137a91ed09461b5848bb2b7341f5c258da3f97853e73662275f047e0c3c20882211b"
},
"depositor": "0x5b38da6a701c568545dcfcb03fcb875f56beddc4"
}

The order is created in pending_deposit. Persist orderId.

Media typeapplication/json
Any of:

Build result for an EVM source chain: the transactions for the depositor’s wallet to send.

object
orderId
required

keccak256(abi.encode(depositor, nonce)), lowercase. Keys every later call: persist it.

string
/^0x[0-9a-fA-F]{64}$/
nonce
required

Decimal uint256 nonce of this deposit. Already part of orderId and the deposit.

string
/^\d+$/
approveTxs

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.

Array<object>
object
to
required

The source token contract.

string
/^0x[0-9a-fA-F]{40}$/
data
required
string
/^0x[0-9a-fA-F]*$/
value
required

Decimal wei; always 0.

string
/^\d+$/
chainId
required

Spoke chain id.

integer
depositTx
required

The deposit, unsigned. Send it exactly once, from depositor, after any approveTxs.

object
to
required

The source chain’s deposit contract.

string
/^0x[0-9a-fA-F]{40}$/
from
required

Equals depositor. A deposit sent from any other account strands the funds.

string
/^0x[0-9a-fA-F]{40}$/
data
required
string
/^0x[0-9a-fA-F]*$/
value
required

Hex wei: the relay fee, sent as the transaction value.

string
/^0x[0-9a-fA-F]*$/
chainId
required

Spoke chain id.

integer
gasLimit
required

Hex gas limit; a fixed value, not an estimate.

string
/^0x[0-9a-fA-F]*$/

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.

Media typeapplication/json
Any of:

The request failed schema validation. Carries message, not error.

object
message
required
string
Allowed value: Validation failed
name
required
string
Allowed value: ValidationException
details
required
Array<object>
object
field
required
string
message
required
string
code
required
string

Example

{
"error": "Quoted terms do not match the signature — re-quote"
}

The X-API-Key header is missing.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

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.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

Example

{
"error": "Unknown or inactive integrator"
}

Source and destination are the same asset.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

Example

{
"error": "Source and destination resolve to the same mToken"
}

The quote’s attestation is older than five minutes. Re-quote; nothing was stored.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

Example

{
"error": "Quoted terms expired at 1787000000"
}

Rate limited per integrator and per IP. No Retry-After; back off and retry.

Media typeapplication/json

Rate limited. No Retry-After; back off and retry.

object
errorCode
required
string
Allowed value: RATE_LIMITED
message
required
string

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.

Media typeapplication/json
Any of:

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

Example

{
"error": "Failed to persist pending order"
}

This deployment cannot take deposits from the source chain. detail.missing names the missing setting.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

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.

Media typeapplication/json

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

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.

Media typeapplication/json
Any of:

An error raised by the route itself.

object
error
required
string
detail
object
minSrcAmount

Smallest deposit that would quote, in source base units.

string
/^\d+$/
maxSrcAmount

Largest deposit that would quote, in source base units.

string
/^\d+$/
maxOutcomeAmount

Largest output that would quote, in destination base units.

string
/^\d+$/
missing

The deployment setting this direction needs.

string

Example

{
"name": "MaintenanceException",
"message": "SystemMaintenance"
}
Retry-After
integer

Seconds to wait. Sent only by the maintenance response.