Skip to main content
Takes the opaque quoteToken from /quote and returns one of three shapes tagged by executionType. See Execution Types for the branch logic. A Hinkal private swap, which only a quote sent with authorizeSteps: true (or onlyVenues naming hinkal) can win, first answers AUTHORIZE: see AUTHORIZE steps below.

Request

string
required
The opaque token from /quote.
string
Late-bound recipient. Only Chainflip, Houdini Swap, Layerswap, Mayan, NEAR Intents, and Rift bind the recipient at execute time. Every other venue fixes it when it prices the quote, and a different address here is rejected with 400 INVALID_REQUEST, so supply it on /quote for those. Houdini Swap pays userAddress when no recipient was given anywhere, so a Houdini swap into a different ecosystem needs one.
string
Late-bound refund address. Chainflip, Houdini Swap, NEAR Intents, and Rift use one given here. Every other venue, including Mayan, Layerswap, and RAVN’s two-hop routes, fixes the refund when it prices the quote, and rejects with 400 INVALID_REQUEST a refund that differs from the one the quote was priced with. If the quote had no refund, they refund where it was priced, normally userAddress, so they reject any other address here. The exception is selling native Bitcoin with no refund on the quote: a venue that can still execute uses the one given here. A Bitcoin-source Rift swap needs a Bitcoin refund address, here or on /quote. Hinkal takes none: the paying wallet recovers a failed Hinkal swap itself, see /recover.
string
Only after an answer with executionType: "AUTHORIZE" (Hinkal): its authorization (hk_ and 32 hex characters), sent with signature and the same quoteToken. Each is single-use and expires at the answer’s expiresAt. Send both or neither: one without the other is 400 INVALID_REQUEST.
string
With authorization: the paying wallet’s signature over that AUTHORIZE answer’s sign, 0x hex.
string
Any unique value per swap attempt (a UUID, say), 1 to 255 printable ASCII characters. Calling /execute again with the same key and quoteToken within 24 hours returns the first answer instead of building the swap again: without it, a retry after a timeout opens a second deposit address at NEAR Intents, Chainflip or Houdini Swap. The same key with another quoteToken is 400 INVALID_REQUEST; a retry while the first call is still running is 503 UNAVAILABLE (retry after error.details.retryAfterSec). Scoped to your key, or to your IP without one. Each answer to an AUTHORIZE step (with authorization and signature) is a request of its own: send a new key with each. executeAndTrack (SDK 0.2.1) sends one itself.
No API key required (see Authentication for the optional higher-limit tiers).

Response

One of three payload types. A TRANSACTION can also carry submit, shown in its own tab, and a Hinkal swap answers AUTHORIZE before its TRANSACTION:

AUTHORIZE steps

Only for Hinkal, and only on a quote sent with authorizeSteps: true (or onlyVenues naming hinkal): without that, no quote ever answers AUTHORIZE. The paying wallet (the quote’s userAddress) signs sign, personal_sign over message or eth_signTypedData_v4 over typedData, and you call /execute again with the same quoteToken, the authorization and the signature, before expiresAt. Step 1 opens a read-only Hinkal session; step 2 places Hinkal’s order; then the TRANSACTION approves and pays the deposit. Signing moves no funds. What to check before each signature: Execution Types.
Send Hinkal’s deposit within 10 minutes of receiving the step-2 AUTHORIZE, or its route may have gone stale and the payout fail. Keep that time yourself: the TRANSACTION carries no deadline. Have the wallet sign step 2 within about 6 minutes: an order signed with under 4 of those minutes left is refused before it’s placed (400 INVALID_REQUEST), so start the swap again from /execute. A swap whose payout fails is not refunded on its own: the paying wallet gets the funds back through POST /recover. Up to 12 Hinkal swaps can be started an hour per IP, or 120 per API key; past that, step 1 answers 502 BUILD_FAILED (“too many private swap attempts”). Calls without a key share their server’s IP, so send your key. A Hinkal quote can’t be executed through the MCP server (400 INVALID_REQUEST) or x402.
On a route where RAVN collects your fee beside the swap (Rift and private swaps on a DEPOSIT; Eco Routes, Layerswap, Mayan routes paying no referrer, Fly Trade’s coin sells outside Ethereum, Base and Polygon, and Hinkal’s deposit on a TRANSACTION), a key that sets a fee also gets fee: { "amount", "address", "chainId", "transaction"? }. deposit.amount, or the transaction, already leaves the fee out. On an EVM chain, send fee.transaction exactly as given: on its own once the swap or deposit has confirmed, or, for an ERC-20 fee, in the same wallet batch or relay as the swap. A Fly Trade coin sell’s fee on Robinhood Chain and Arbitrum is WETH minted to RAVN (fee.transaction sends the coin to WETH’s depositTo; the quote’s fee.token is WETH), so it may ride in the swap’s batch too: calls is the swap then the fee, with batch: true. Any other native-coin fee must be a plain transaction of its own, except beside a NEAR Intents deposit on Ethereum, Base or Polygon. Then pass the on-chain hash of the transaction that carried it as txHash to /status. From Bitcoin, or a Solana deposit, there’s no transaction: the fee goes into the deposit’s own transaction. On a Solana TRANSACTION RAVN builds or adds its fee to (Mayan, Layerswap, Jupiter under 50 bps) there’s no fee at all: it’s already inside transaction.serialized, so send it from the signing wallet itself. See Pricing. On JS/TS (SDK 0.1.15 or later, 0.2.5 recommended), executeAndTrack pays a TRANSACTION’s fee, depositFeePayment says what to pay beside a DEPOSIT, and from 0.2.4 sendDeposit: true has executeAndTrack send the DEPOSIT and its fee itself. From 0.2.5 it follows batch: swap and fee in one sendCalls when it’s true, the fee after the swap’s receipt when it’s false.
Every type returns a quoteToken. Use that token, not the one from /quote, for /submit-signature, /submit-transaction, and /status. It carries anything bound at execute time: a two-hop RAVN route (venue.id is compose) is rebound to its second hop here, and /submit-signature refuses that quote’s original token.
TRANSACTION and SIGNATURE can carry an optional statusRef, the venue’s own tracking key (on a two-hop RAVN route, the packed hop-2 handle). On TRANSACTION, poll /status with that statusRef when present, otherwise with the hash of the transaction you broadcast. A TRANSACTION with submit you don’t broadcast: poll with its statusRef when present, otherwise with the txHash that /submit-transaction returns (Jupiter). On SIGNATURE, poll with the statusRef that /submit-signature returns. Poll it until terminal. On same-chain venues that report is what settles the swap in RAVN’s ledger. With your API key RAVN also looks for an unreported swap itself for two hours, but report it anyway: see Settlement.
Every TRANSACTION, SIGNATURE and DEPOSIT carries swapId (except on a sandbox quote; an AUTHORIZE step has none, since nothing is booked until its TRANSACTION): this swap’s id in RAVN’s ledger, which settlement webhooks name. Keep it with your own record of the swap.
Ready to sign. The fields above say what to send; these hand it to you built, RAVN’s fee included:
  • calls (EVM TRANSACTION and DEPOSIT): every transaction to send, in order, each { to, data, value, chainId }: approval.reset, approval, the swap (on a DEPOSIT, the transfer of deposit.amount to deposit.address), then fee.transaction. Send each once the one before it has succeeded, and stop if one reverts. When batch is true they may instead go in one atomic wallet batch (EIP-5792 wallet_sendCalls with atomicRequired: true): simulate it first, and send them one by one if that fails or the swap’s call needs more than ~400k gas. batch is false for Bebop, THORChain, two-hop routes, sales of the chain’s native coin (except a Fly Trade coin sell whose fee is WETH minted to RAVN, on Robinhood Chain and Arbitrum), and any other native-coin fee or deposit except at NEAR Intents.
  • deposit.psbt (Bitcoin, from a native SegWit bc1q… userAddress): the deposit, RAVN’s fee as a second output, and change back to your user, as a ready-to-sign PSBT (base64) from their confirmed coins. Sign and broadcast it as is. Absent when it can’t be built (another address type, too little balance, an address mempool.space won’t list): pay as the fields say.
  • deposit.transaction (a Solana deposit carrying RAVN’s fee, a private swap): the deposit and the fee as one unsigned transaction from your user’s wallet (base64). Sign and send it within a minute: its blockhash then expires. A retry with the same Idempotency-Key returns this same transaction, so for a fresh one call /execute without the key (a new order: send to its deposit only).
approval appears on TRANSACTION and SIGNATURE when the input is an ERC-20. On TRANSACTION, send it and wait for it to be mined before transaction, or the swap reverts on transferFrom. It is left out when your user’s on-chain allowance already covers it (and kept when RAVN can’t read that allowance, so skip it when your own read covers it); an approval.reset (Ethereum USDT with a partial allowance) goes first. On SIGNATURE (CoW, Bebop), land it before submitting or the order is accepted and silently never fills. See Execution types.
A TRANSACTION carrying submit (only on quotes made with broadcastViaRavn: true) must not be broadcast. Sign transaction.serialized without sending it, and post it with this response’s quoteToken to /submit-transaction. A Layerswap memo payment is checked there before it is sent: broadcast directly, it skips that check, a smart-contract wallet can wrap the payment so Layerswap never matches it, and the funds sit at Layerswap. A Jupiter order is handed to Jupiter to land: one its market maker or gas sponsor signs too can’t land if you broadcast it. A Jupiter order carrying your fee under 50 bps comes without submit: broadcast that one yourself.
On Solana (chainId -2), transaction.serialized is a base64 versioned transaction. Simulate and send it with commitment confirmed (e.g. sendRawTransaction(tx, { preflightCommitment: 'confirmed' })): it’s built on a confirmed blockhash, and a finalized preflight or simulation reports BlockhashNotFound for its first ~13 s.
deposit.expiresAt (ISO 8601), when present, is when the deposit must have arrived, confirmed on the origin chain, not when it must be sent. Houdini Swap, for example, sets 7 to 15 minutes. Send well ahead: a deposit that lands later reaches an expired order, the venue no longer honours the quoted price, and recovering the funds can need the venue’s support.
Up to 10 minutes past the quote’s expiresAt, RAVN prices the same route again and builds that only if it still meets the minimum the quote showed (see expiresAt on /quote). Each such call prices again, and at a deposit venue opens a new deposit, so send an Idempotency-Key to get the first answer back on a retry. 503 UNAVAILABLE means the quote can’t be looked up right now: retry the same call after error.details.retryAfterSec. Returns 410 QUOTE_EXPIRED when the new price misses the minimum, or RAVN no longer holds the quote; 400 QUOTE_INVALID if the token is malformed or tampered with, or 400 QUOTE_NOT_EXECUTABLE if the quote was a preview only (executable: false on /quote). For these three, request a fresh quote and retry. QUOTE_NOT_EXECUTABLE names the address the preview was priced without, and error.details.missing lists it: destinationAddress, refundAddress, or both, to add to the new quote. It’s empty when the preview has another cause, such as a wallet holding less than the amount.
If the quoteToken came from a sandbox quote, the execution carries "sandbox": true and nothing is opened at any venue. NEAR Intents, Rift, Garden, Layerswap, Houdini Swap, Hinkal (with no AUTHORIZE steps), Chainflip, RAVN’s two-hop routes and THORChain from Bitcoin return a stand-in DEPOSIT to your user’s own wallet; every other venue returns its real payload, built live, which swaps for real if sent or submitted to the venue yourself. Never send a sandbox execution. See Sandbox Mode.