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.
{
"turbineSettlerAddress": "0x49e9a8ea9b6c05d5b2307538d159350a5aea73ac",
"turbineSignerAddress": "0x89c740fea6bd1df86d0f8dff3f4c4c23cb598890",
"lpHookAddress": "0x40bd6d8c59d43f6c345d79b17234d9b0e781a088",
"lpRouterAddress": "0x4bd3f2ffc321f3ba4c3b31708212b76922f805a2",
"poolManagerAddress": "0x000000000004444c5dc75cb358380d2e3de08a90",
"submitSettlements": true,
"siweDomain": "app.turbine.exchange",
"siweUri": "https://api.turbine.exchange/api"
}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": "0x15ccf087670a46e54d71bd1de429dde0c372d17f"
}No body. Clears the server-side session.
Submit a single order. Body for standard (non-smart-callback) orders:
{
"order": {
"owner": "0x15cCf087670A46e54d71Bd1DE429dDE0c372D17f",
"sellToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"buyToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"sellAmount": "16000000000000000",
"minBuyAmount": "33000000",
"midPriceDelta": -10,
"startTime": "1776048000",
"endTime": "1776051600",
"partialFill": true,
"callData": "0x",
"callDataTarget":"0x0000000000000000000000000000000000000000",
"salt": "0xa2a03821c140f9baec1e0a1f3f292a27d9f5ce25db971aa78bdcb3592bb74415"
},
"signedPermit": {
"signature": {
"r": "0xf5e127cbb14083788e6bcbf26b35636a8909b5f498eca7d0d3d661ebbd26d801",
"s": "0x3dc945cafffccba65336fe95769a9b548cd16d65612e050ba2838a242238e81c",
"yParity": false,
"v": "0x1b"
},
"permit": {
"details": {
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"amount": "1461501637330902918203684832716283019655932542975",
"expiration": 1776051600,
"nonce": 0
},
"spender": "0x49E9A8EA9B6C05d5B2307538D159350A5aEa73aC",
"sigDeadline": "1776051600"
}
}
}Response:
{ "orderHash": "0xe5e705c088dfce48ebce881c45ceaa33ddf00087af0d23eccc91393d64c12634" }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". midPriceDeltais a signed int, not a string. Negative values (MM "earn" side) and positive values (taker "fast" side) are both valid. Range-10000..+10000= ±100%.- 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.
Same payload wrapped in an array. Response is an array of
{ orderHash }. Each order is independently signed with its own
Permit2 permit.
{ "orderHash": "0xe5e705c088dfce48ebce881c45ceaa33ddf00087af0d23eccc91393d64c12634" }Response:
{ "orderHash": "0xe5e705c088dfce48ebce881c45ceaa33ddf00087af0d23eccc91393d64c12634" }{ "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 strings seen in the wild:
"Active"— resting in the book, no fills yet.
Other enum values (Filled, PartiallyFilled, Cancelled, Expired)
are inferred from the SDK's TypeScript typing but not directly
verified at the time of writing. Please PR if you observe them.
execution[].sold_amount / bought_amount / surplus_buy_amount are
hex-prefixed bigint strings — parse with a base-0 int parser.
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. See
fees.md for how to interpret this — spoiler: it works out
to ~0.99 bps (0.0099%) of the mid-price notional, flat.
{
"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.