Base URL: https://api.turbine.exchange/api
All authenticated endpoints (everything except /status and /config)
require a valid SIWE session cookie — see
authentication.md.
All request and response bodies are JSON unless stated otherwise.
Bigint fields in responses are serialized as 0x-prefixed hex strings
(the official JS SDK's bigIntReplacer). Input accepts both decimal
and hex strings; strictly hex for inputs is safer because the SDK
uses it. See wire-format.md.
Health check. Returns text "OK from Turbine" with HTTP 200.
Returns the runtime contract addresses and SIWE fields. Call this on every startup, verify the returned values against hard-coded pins, and halt if they do not match.
{
"version": "0.114.1",
"turbineSettlerAddress": "0xbb3e81c0563dc61719696475f5c7b5e011a73f8a",
"turbineSignerAddress": "0x89c740fea6bd1df86d0f8dff3f4c4c23cb598890",
"lpHookAddress": "0xa44ff524f78858e015fcca322cb7d16aeb89a088",
"lpRouterAddress": "0x8e7cc22eda4e2d3a8275fd88cf061681b42ce3d1",
"poolManagerAddress": "0x000000000004444c5dc75cb358380d2e3de08a90",
"submitSettlements": true,
"siweDomain": "app.turbine.exchange",
"siweUri": "https://api.turbine.exchange/api",
"tokens": [ /* ... */ ]
}version reports the deployment (0.114.1 at the time of writing).
config.tokens is the supported-token list returned by the server
(~401 entries, each with CEX oracle mappings such as
binance/bingx/bitget/coinbase/kraken/kucoin/okx). Do not
hard-code it — read it from /api/config. The pair used throughout
these examples is WETH
(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) / USDC
(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48).
No body. Returns a JSON string:
"b3a9f0e1c8d7a6b5..."Consume it in the next /verify call — nonces are single-use.
Body:
{
"message": "<SIWE message text>",
"signature": {
"r": "0x...",
"s": "0x...",
"yParity": "0x0",
"v": "0x1b"
}
}Empty-body 200 on success, sets cookie. See authentication.md for full details.
{
"authenticated": true,
"address": "0x1111111111111111111111111111111111111111"
}address is your authenticated wallet (the placeholder above stands
in for <your wallet address>).
No body. Clears the server-side session.
Submit a single order. Body for standard (non-smart-callback) orders:
{
"order": {
"owner": "0x1111111111111111111111111111111111111111",
"sellToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"sellAmount": "16000000000000000",
"minBuyAmount": "33000000",
"spreadCurve": {
"startDeltaBps": -10,
"endDeltaBps": -10,
"points": []
},
"startTime": "1776048000",
"endTime": "1776051600",
"partialFill": true,
"callData": "0x",
"callDataTarget":"0x0000000000000000000000000000000000000000",
"salt": "0x1111111111111111111111111111111111111111111111111111111111111111"
},
"signedPermit": {
"signature": {
"r": "0x1111111111111111111111111111111111111111111111111111111111111111",
"s": "0x2222222222222222222222222222222222222222222222222222222222222222",
"yParity": false,
"v": "0x1b"
},
"permit": {
"details": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"amount": "1461501637330902918203684832716283019655932542975",
"expiration": 1776051600,
"nonce": 0
},
"spender": "0xbb3e81c0563dc61719696475f5c7b5e011a73f8a",
"sigDeadline": "1776051600"
}
}
}The spreadCurve above is a flat curve (startDeltaBps == endDeltaBps,
no interior points), equivalent to the old fixed midPriceDelta of
-10 bps. spender equals the turbineSettlerAddress returned by
/api/config. See the Spread curves section below for the full
shape.
Response:
{ "orderHash": "0x1111111111111111111111111111111111111111111111111111111111111111" }Important details:
signedPermit.signature.yParityis a JSON boolean (trueorfalse). Sending a string"0x0"/"0x1"fails with HTTP 422. This is different from/verify— see wire-format.md.- Minimum order size is enforced at $30 USD. Smaller values fail
with HTTP 400
INPUT_VALIDATION_ERRORand a message like"Sell amount X is worth ~$19.97 which is less than $30". spreadCurvereplaces the old scalarmidPriceDelta(which is gone in v0.114). It is a delta curve over the order's time window — see Spread curves below. EachdeltaBpsis a signed int in[-10000, 9999](1 unit = 1 bp = 0.01%); negative is maker-favorable ("earn"), positive is pay-to-fill ("fast").- Permit2
amountin the example ismaxUint160 = 2^160 - 1(unlimited approval); you can specify a tighter amount if you want per-order cap. callData/callDataTargetnon-empty makes this a "smart order"; thensignedPermitis omitted — see the smart-order section below.
A spreadCurve carries the order's price delta as a piecewise-linear
curve over the order's normalized time window:
{
"startDeltaBps": -10,
"endDeltaBps": -10,
"points": [ { "windowBps": 5000, "deltaBps": -5 } ]
}windowBpsis normalized time from0(=startTime) to10000(=endTime); interior knots are in[1, 9999].deltaBpsis signed,[-10000, 9999](1 unit = 1 bp = 0.01%).startDeltaBpsis the delta atwindowBps = 0,endDeltaBpsatwindowBps = 10000. Interior knots go inpoints.- The effective delta at time
nowis linear interpolation between the surrounding knots. pointsmay hold up toMAX_SPREAD_CURVE_POINTS = 1024knots (an SDK guard; the backend may be stricter by order duration / block interval).
The official JS SDK (turbine-sdk/src/spreads.ts) ships two builders:
-
constant(deltaBps)→{ startDeltaBps: d, endDeltaBps: d, points: [] }. A flat curve — this reproduces the old fixedmidPriceDeltabehavior. -
auto({ fastSpreadBps, deltaBps?, yoloBps? })→ a 4-knot ramp ("auto-spread", a new order shape).fastSpreadBpsis required and positive (the target "fast"/AMM spread reached by the end of the window). The anchors are:windowBpsdeltaBps0yoloBps1000-fastSpreadBps5000fastSpreadBps - deltaBps10000fastSpreadBps + deltaBpsDefaults:
deltaBps = max(1, round(fastSpreadBps * 0.2)),yoloBps = -1000. The builder rejects parameters that break monotonicity or exceed the domain: it requiresyoloBps < -fastSpreadBps,deltaBps < 2 * fastSpreadBps, andfastSpreadBps + deltaBps <= 9999.An auto curve starts maker-favorable and ramps to a positive (pay-to-fill) end, so the order is designed to settle within its window rather than rest as a stale limit order.
Same payload wrapped in an array. Response is an array of
{ orderHash }. Each order is independently signed with its own
Permit2 permit.
{ "orderHash": "0x1111111111111111111111111111111111111111111111111111111111111111" }Response:
{ "orderHash": "0x1111111111111111111111111111111111111111111111111111111111111111" }{ "orderHashes": [ "0x...", "0x..." ] }Response is an array:
[
{
"hash": "0x...",
"status": "Active",
"execution": [
{
"tx_hash": "0x...",
"block_number": 18123456,
"sold_amount": "0x1a",
"bought_amount": "0x2c",
"surplus_buy_amount":"0x0"
}
]
}
]status is one of the v0.114 OrderStatus values (SDK
types/orders.ts):
Active— resting in the book, no fills yet.Adding— order is being added, pre-Active.FilledPartiallyFilledPendingCancellationCanceledExpiredInvalidUnknown
The SDK groups Adding, Active, and PendingCancellation as
"pending". Only Active is directly verified here; the other
strings are taken from the SDK enum but not directly verified at
the time of writing. Please PR if you observe them.
execution[] amounts are hex-prefixed bigint strings — parse with a
base-0 int parser. Be tolerant of casing: a legacy snake_case form
(tx_hash, block_number, sold_amount, bought_amount,
surplus_buy_amount) and a camelCase form (txHash, blockNumber,
soldAmount, boughtAmount, and a surplus field — the SDK reads it as
surplusBuyAmount) have both been referenced. The official SDK parses
the camelCase shape. The exact on-the-wire casing is not fully
verified — read whichever key is present rather than relying on one
form.
Quote the platform fee for a prospective order without submitting
it. Body is the raw OrderIntent (no signedPermit):
{
"owner": "0x...",
"sellToken": "0x...",
...
}Response is a hex-prefixed bigint string denominated in the buy token's atomic units:
"0xd8d"Value is 0xd8d = 3469 atomic units in the example. This endpoint is
the source of truth for the fee — quote it per order rather than
assuming a fixed rate. See fees.md for how to interpret the
returned amount.
{
"addLiquidity": { /* AddLiquidityIntent */ },
"permitTokens": {
"signature": { "r": "0x...", "s": "0x...", "yParity": false, "v": "0x1b" },
"permit": { /* PermitBatch */ }
}
}{
"removeLiquidity": { /* RemoveLiquidityIntent */ },
"permitLpToken": {
"signature": { "r": "0x...", "s": "0x...", "yParity": false, "v": "0x1b" },
"permit": { /* PermitSingle */ }
}
}{ "intentHashes": ["0x...", "0x..."] }Response:
[
{ "hash": "0x...", "status": "Pending" }
]Known statuses per the SDK's LiquidityIntentStatus enum:
Pending, Invalid, Expired, Executed, PendingCancellation,
Canceled.
LP details (reserve reads, pool registration, permit batch signing)
happen on-chain via the lpHookAddress and lpRouterAddress
contracts — not covered here. See the SDK source or open an issue if
you need specifics.
If your OrderIntent has callData != "0x" and callDataTarget != 0x0, the server treats it as a "smart order" (intent with a
post-execution callback). Payload then omits the permit:
{ "order": { /* intent with callData, callDataTarget */ } }No signedPermit. The SDK's TurbineClient.createAddOrderData makes
this distinction automatically. We have not exercised this path.
Errors come in two observed forms.
HTTP 4xx with JSON body:
{
"code": "INPUT_VALIDATION_ERROR",
"message": "Sell amount 9100000000000000 is worth ~$19.971538 which is less than $30"
}HTTP 4xx with plain-text body (serde deserialization errors):
Failed to deserialize the JSON body into the target type:
signedPermit.signature.yParity: invalid type: string "0x0",
expected a boolean at line 1 column 698
Your client should surface both forms. The official SDK wraps them in
TurbineError objects.
Not observed in our testing. If you hit one, please open an issue with the response headers so we can document it here.