/v3/limit - Place and manage limit orders
Offer your users price-contingent swaps that execute asynchronously when the market reaches their target.
Pre-release. This API is in active development and request/response schemas may still change before GA. Only test with small amounts — orders placed here route real liquidity through our providers, so any funds committed are at real risk while the service stabilises.
The SwapKit Limit Order API lets integrators offer their users price-contingent swaps: the order rests until the market reaches the target price, then settles asynchronously. All endpoints sit under /v3/limit/*, every request must carry an x-api-key header, and all payloads are JSON.
Integrators do not choose the provider. SwapKit routes the pair server-side and returns the chosen provider on the quote. The wallet-signing shape differs per provider, and the service surfaces that difference on the /v3/limit/build response.
Signing models
Every provider fits one of two signing models. /v3/limit/build tells you which one applies to a given order by populating exactly one set of fields:
Signed intent
typedData populated, tx and txMeta null
Signs an off-chain payload. Nothing goes on-chain at build time — the provider holds the signed order until a filler matches it.
Deposit
tx and txMeta populated, typedData null
Signs and broadcasts a transaction. The deposit is the order: its parameters are encoded into the transaction, and the resulting tx hash is the receipt.
Providers
1inch
Signed intent
Same-chain EVM swaps
ETH, ARB, AVAX, BASE, BSC
Available — more EVM chains rolling out
Harbor
Deposit
Cross-chain between BTC, ETH and USDT
BTC, ETH (+ USDT)
Available — BTC ↔ ETH.ETH and BTC ↔ ETH.USDT pairs
THORChain
Confirmed at launch
Native cross-chain across 11 chains
BTC, ETH, LTC, ATOM, DOGE, AVAX, BSC, SOL, BASE, TRX, XRP
Coming soon — broadens BTC, LTC, Cosmos, Doge, TRX, XRP
Jupiter
Confirmed at launch
Solana native assets
SOL
Coming soon — Solana spot liquidity
New providers are added without a breaking change: the endpoints, the order object, and the status lifecycle stay identical, and the only per-provider variation is which signing model the /build response uses.
Endpoint index
GET
/v3/limit/tokens
Supported assets and pairs per provider.
1. Price the pair
Method: POST
URL: https://api.swapkit.dev/v3/limit/quote
Returns a market price for the pair, computes the user's limitPrice deviation from spot, and caches a routeId for the subsequent /v3/limit/build call.
Request schema
sellAsset
string
Yes
The asset being sold (e.g. "ETH.ETH")
buyAsset
string
Yes
The asset being bought (e.g. "BTC.BTC").
sellAmount
decimal string
No
Provide any two of sellAmount / buyAmount / limitPrice; the third is derived. Human-readable decimal, not base units — selling 10 USDT is "10", not "10000000".
buyAmount
decimal string
No
Derived from the other two if omitted. Same human-readable decimal convention as sellAmount.
limitPrice
decimal string
No
buyAsset per 1 sellAsset, in human-readable decimal.
sourceAddress
string
No
Screened for AML at quote time and pre-filled into the /v3/limit/build hint.
destinationAddress
string
No
Same treatment as sourceAddress.
affiliateFee
int (0–1000 bps)
No
Overrides the API key default.
expiresAt
int (unix seconds)
No
Defaults to now + 3 days.
providers
enum[]
No
Restrict routing to these providers. Omit to consider every provider that supports the pair.
Response schema
Top level
quoteId
string
UUID for this quote response
routes
LimitRoute[]
A single-entry array.
Per route
routeId
string
UUID of this specific swap response. Pass this to /v3/limit/build.
provider
enum
Provider used, chosen server-side.
sellAmount, buyAmount, limitPrice
decimal string
Resolved values, including the leg you didn't provide.
spotPrice
decimal string
Current market reference price.
effectiveFillPrice
decimal string
The price the market must reach for the order to fill: spotPrice × (1 + feeGapBps / 10_000). The gap is takerFeeBps + integratorFeeBps on 1inch, where the integrator fee is charged on the taker side; on Harbor it's takerFeeBps alone, since the integrator fee is settled out of proceeds and doesn't affect the fill threshold. Equals spotPrice when the gap is zero.
takerFeeBps
number
Protocol-side taker fee in bps a whitelisted filler pays on top of the taking amount. Excludes the integrator fee, which is reported separately as integratorFeeBps.
spotPriceDeviationBps
number
Signed bps delta of limitPrice vs effectiveFillPrice — not vs raw spotPrice.
Positive: the limit sits above the fill threshold, so the order waits for the market to move.
Negative: it's at or below the threshold and would fill immediately at a worse rate than a market swap.
minExpirationSeconds, maxExpirationSeconds
number
Bounds for the expiresAt you may pass to /v3/limit/build.
integratorFeeBps
number
Affiliate fee applied to this route.
nextActions
object[]
Data needed for the next request in the flow (/v3/limit/build call).
Compare against effectiveFillPrice, not spotPrice — it's the threshold that triggers a fill, and spotPriceDeviationBps is measured from it.
expiresAt is an absolute unix timestamp, but the bounds are durations: expiresAt − now must fall within [minExpirationSeconds, maxExpirationSeconds]. Outside that, /build returns limitOrderExpirationOutOfBounds (400).
Warnings
Each entry in warnings[] is a structured object — the same shape swap quotes already use — so you can render a short display label with a longer tooltip behind it. tooltip is optional; null-check it. The array is always present and may be empty, and the cached route carries its warnings through to the /v3/limit/build response as well.
At most one price warning fires per quote. Both are measured against effectiveFillPrice, not spotPrice:
limitPriceBelowSpot—spotPriceDeviationBps ≤ -100. The limit sits 1% or more below the effective fill price, so the order fills immediately at a clearly worse rate than a market swap.limitPriceWithinFeeGap—-100 < spotPriceDeviationBps ≤ 0, and the fee gap is non-zero. The limit sits inside the fee gap: it still fills immediately, but only modestly off-market. Raising the limit by|spotPriceDeviationBps|bps or more puts it above the threshold, where it rests as a genuine limit order.
Both mean the order fills on submission rather than resting — surface either to the user before they sign. Render the display string with tooltip behind it rather than switching on code, and treat code as an open enum: new values ship with new providers.
Errors
400
insufficientLiquidity
The provider could not return a market rate for the pair, or the market rate / requested amount evaluated to zero. Surfaced as "Insufficient liquidity for trade".
400
invalidSourceAddress
sourceAddress was provided but doesn't match the sell-chain address format. Per-chain validation fires at /quote rather than waiting for /build.
400
invalidDestinationAddress
destinationAddress was provided but doesn't match the buy-chain address format. Same early-validation behaviour.
400
limitOrderAmountAmbiguous
Not exactly two of sellAmount / buyAmount / limitPrice were supplied.
400
limitOrderUnsupportedPair
No limit-order provider implements this pair at all.
400
limitOrderUnsupportedChain
Limit orders aren't offered on that chain.
400
limitOrderChainMismatch
The pair spans two chains on a path that requires both assets on one.
401
apiKeyInvalid
Missing or unrecognized x-api-key header.
502
limitOrderProviderError
The upstream provider rate-limited, returned a 5xx, or failed in transport while pricing. Deliberately generic. Worth retrying.
503
limitOrderActionUnavailable
A provider supports the pair, but the action is currently disabled for one of its chains. Ops-controlled, so it can clear without a change on your side.
2. Build the signing artifact
Method: POST
URL: https://api.swapkit.dev/v3/limit/build
Validates and screens both addresses per-chain, resolves the token spender where the chain needs one, and runs the provider build, deep address screens, and approval check in parallel. Persists the order with status = PENDING.
Request schema
routeId
string
Yes
The ID of the route to build the order from. Obtained from a previous /v3/limit/quote response
sourceAddress
string
Yes
Blockchain address to send the asset from. Must be a valid address for the sell asset's chain. Becomes the order's maker.
destinationAddress
string
Yes
Recipient blockchain address to send the asset to. Must be a valid address for the buy asset's chain. Becomes the order's receiver.
expiresAt
int (unix seconds)
Yes
Must fall inside [minExpirationSeconds, maxExpirationSeconds] from /v3/limit/quote.
allowPartialFill
bool
No
1inch only, and must be true (the default). Disabling either flag forces LOP v6 bit-invalidator mode, which the orderbook accepts only for RFQ orders, so /build returns limitOrderUnsupportedFillFlags (400). Harbor ignores both.
allowMultipleFills
bool
No
Same constraint as allowPartialFill
usePermit2
bool
No
Currently ignored server-side — a placeholder for an upcoming Permit2 two-phase build flow on EVM. Until it ships, EVM token orders use the approvalTx path. Default false.
Build response schema
orderId
string
UUID of this specific order. Pass it to /v3/limit/submit and every later call.
orderHash
string
Canonical on-chain identifier of the order, 0x-prefixed.
typedData
object (optional)
Off-chain payload for the wallet to sign. Signed-intent orders only — null on deposit orders. See below.
tx
varies (optional)
Ready-to-sign deposit transaction. Deposit orders only — null on signed-intent orders. See below.
txMeta
object (optional)
Broadcast hints for tx. Deposit orders only — null on signed-intent orders. See below.
isApproved
boolean (optional)
Whether the sell-side token allowance is already in place. Omitted when no approval is part of the flow — See below.
approvalTx
object (optional)
Present when a token approval transaction must be submitted before the order can be signed or broadcast. See below.
warnings
Warning[]
Potential warnings about this order, carried over from /v3/limit/quote. Warnings never block the response.
nextActions
object[]
Data needed for the next request in the flow (/v3/limit/submit call).
Response — the two signing models
You always get exactly one of the two shapes below, never both and never a mix — see signing models.
1inch today.
The wallet signs typedData, then you post the resulting signature to /v3/limit/submit as { orderId, signature }.
Harbor today.
The wallet signs and broadcasts tx, then you post the on-chain hash to /v3/limit/submit as { orderId, depositTxHash }.
The typedData object
Returned for signed-intent orders only. A structured payload the wallet signs without broadcasting — today an EIP-712 document with domain, types, primaryType, and message. Pass it to the wallet unmodified and post the resulting signature to /v3/limit/submit; don't reconstruct or reorder it, since the signature is taken over the exact payload.
The tx field
Returned for deposit orders only. Its shape depends on the sell chain, and txMeta tells you how to handle it — the same pattern /v3/swap uses:
txMeta.txType
string
How to sign and broadcast tx. "evm" — tx is an object with { from, to, value, data }. "psbt" — tx is a base64 PSBT string. More values ship with new chains.
txMeta.chainId
number (optional)
EVM chain the deposit is broadcast on, as a number (1, 42161) — not a string. Present only when txMeta.txType is "evm"; omitted entirely on the psbt path, where the chain is implied by the PSBT. Read the chain from txType rather than expecting this field.
txMeta.memo
string
The order memo encoded into the deposit, exposed so you can verify it before signing. Present for providers that encode order parameters in a memo.
The approvalTx object
Present when the sell asset is a token on a chain that requires an allowance to a spender contract, and the maker's current allowance is below the order amount. Same shape as the approvalTx returned by /v3/swap — broadcast it and wait for confirmation before signing or broadcasting the order.
approvalTx.to
string
Token contract address to send the approval
approvalTx.from
string
User wallet address.
approvalTx.value
string
ETH value — always "0" for approvals.
approvalTx.data
string
Encoded approve() call data.
approvalTx.gasLimit
string
Optional. Estimated max gas units for the approval transaction, as a hex quantity string
approvalTx.gasPrice
string
Optional. Gas price in wei per gas unit, as a hex quantity string. Legacy (type-0) gas pricing.
The isApproved field
isApproved is only populated when a separate token approve() is part of the flow. Treat it as a tri-state:
isApproved: true
Nothing extra to broadcast — though not a guarantee that the allowance was verified. See below.
isApproved: false
An approval is required and the wallet must broadcast the accompanying approvalTx before signing the order.
Field omitted
No approval is required — the sell asset is a native asset, or the chain has no allowance model (UTXO chains, for instance).
Whether the field appears is a property of the sell asset and its chain, not of the provider.
Treat a missing isApproved as "nothing to broadcast", not as false.
How the allowance check is evaluated
isApproved is not a general "is this token approved" flag. It reports whether this order can be pulled, on the same amount-scoped and spender-scoped terms /v3/swap uses — see How the allowance is read for the mechanics and the staleness caveat.
So a token the wallet already shows as approved can still come back false: approving 1 USDT and then building a 2 USDT order returns false, and an allowance granted for an earlier order on a different provider does not carry over, because each provider pulls through its own contract.
The check is advisory as well — if the read fails outright, the build returns isApproved: true rather than failing with it, so true is not proof the allowance exists.
Errors
404
limitOrderQuoteNotFound
The quoteId is unknown or has expired. Re-price with /v3/limit/quote.
404
limitOrderRouteNotFound
The routeId isn't one of the routes that quote returned.
400
limitOrderExpirationOutOfBounds
expiresAt sits outside the quote's expiration bounds. The message carries both.
500
limitOrderBuildFailed
The provider accepted the pair but failed to build the order.
3. Submit the signed artifact
Method: POST
URL: https://api.swapkit.dev/v3/limit/submit
Attaches the wallet signature (signed-intent orders) or the on-chain deposit tx hash (deposit orders) to the order, advancing it from PENDING to SUBMITTED. The poller then watches for provider acknowledgement and flips SUBMITTED → OPEN.
Request schema
Send signature or depositTxHash, depending on which artifact /v3/limit/build returned.
orderId
string
All orders
The ID of the order to submit. Obtained from a previous /v3/limit/build response
signature
string
Signed intent
Wallet signature over the typedData returned by /v3/limit/build. Relayed to the provider's orderbook.
depositTxHash
string
Deposit
Hash of the broadcast deposit transaction. Accepted case-insensitively and stored lowercase — it surfaces back as depositHash on /v3/limit/orders/:orderId.
Submit response schema
orderId
string
UUID of this specific order.
orderHash
string
Canonical on-chain identifier of the order, 0x-prefixed.
createdAt
ISO 8601 string
When the order was created.
One shot — the order must still be PENDING. Re-submitting an order, submitting one that has already advanced to SUBMITTED / OPEN / FILLED, or submitting one the stale-unsubmitted sweep has moved to EXPIRED, all return 409 with error code limitOrderInvalidState. Call /build and /submit back-to-back — the grace window before the sweep fires is 1 hour by default.
Submit errors
The orderbook rejects most bad submissions for a client-side reason. Those are reported as 4xx and are not worth retrying — only a genuine upstream fault is a 502.
451
limitOrderMakerBlacklisted
The orderbook refuses orders from this maker address — a compliance decision made upstream, not by SwapKit's screening. Permanent for the address; the order can never be submitted by retrying.
400
limitOrderInsufficientAllowance
The maker hasn't approved enough of the sell token for the limit-order router. Recoverable: re-run /v3/limit/build and broadcast the returned approvalTx, then submit again.
400
limitOrderMaxOrdersExceeded
The maker already holds the orderbook's maximum of simultaneously valid orders (1inch: 100). Recoverable: cancel an existing order first. The message carries the cap.
409
limitOrderInvalidState
The order is already in the book, or has advanced past PENDING. Same code as the re-submit guard above.
400
limitOrderRejected
Any other client-side rejection SwapKit doesn't model individually — bad signature, expired, insufficient balance. Carries the orderbook's own description.
502
limitOrderSubmissionFailed
A genuine upstream fault: the orderbook returned a 5xx, or the call failed in transport with no status. This one is worth retrying.
4. Single-order detail
Method: GET
URL: https://api.swapkit.dev/v3/limit/orders/:orderId
Returns one order. Syncs with the upstream provider on every call before returning, so the response always reflects post-sync state.
Order object
orderId
UUID string
UUID of this specific order. Stable across the order's lifetime.
orderHash
string
Canonical order identifier, 0x-prefixed, stable for the order's whole lifetime. Signed-intent orders: the digest of the signed payload. Deposit orders: a deterministic hash over the order parameters — it is not replaced by the deposit tx hash after /submit; that hash surfaces separately as depositHash.
chainId
string enum
ChainId of the sell asset. Numeric-string for EVM ("1", "42161", "8453", …); slug for non-EVM ("bitcoin", "solana", "thorchain-1", …).
provider
enum
Provider that routed this order.
maker
string
Blockchain address to send the asset from — the address that owns the order on the sell chain. Format varies per chain (EVM hex, BTC bech32, etc.).
receiver
string or null
Recipient blockchain address to send the asset to on the buy chain. Nullable only for legacy orders created without one.
sellAsset
string
The asset being sold (e.g. "ETH.ETH")
buyAsset
string
The asset being bought (e.g. "BTC.BTC").
sellAmount
decimal string
Amount of the sell asset. Human-readable decimal, not smallest units — unlike /v3/quote and /v3/swap.
buyAmount
decimal string
Amount of the buy asset the order is resting for. Same human-readable decimal convention as sellAmount.
limitPrice
decimal string
buyAsset per 1 sellAsset, in human-readable decimal.
filledSellAmount, filledBuyAmount
decimal string
"0" before any fill. On providers that settle all-or-nothing they jump straight to the full amounts; on providers that support partial fills they climb incrementally.
txHashes
TxHash[]
On-chain transactions in the order's lifecycle, chronological. Always present; [] before anything lands. Populated by provider sync or backfill.
usdValueOpen
string or null
Sell-side notional in USD at build time. Approximate (cached price); null if the lookup missed.
usdValueClose
string or null
Realized buy-side notional in USD at the terminal transition. Same approximation; null until the order is terminal.
status
enum
PENDING, SUBMITTED, OPEN, PARTIAL, FILLED, CANCELLED, EXPIRED, FAILED. See the order status lifecycle.
fees
Fee[]
List of fees applied to the order (liquidity, affiliate, service, network) — see below. Pre-fill: projected amounts. Post-fill: actual settled values where the provider reports them.
depositHash
string?
L1 tx hash of the deposit, lowercased. Chain-native format (0x-prefixed on EVM, raw 64-hex on Bitcoin). Populated by the provider sync once the deposit lands; omitted until then, and on models with no deposit leg.
withdrawHash
string?
L1 tx hash of the settlement (withdraw), lowercase 0x-prefixed. Populated after the order fills; omitted before.
expiresAt
ISO 8601 string
TTL. SwapKit flips the order to EXPIRED locally once expiresAt is in the past; providers that escrow funds refund them at this point.
createdAt, updatedAt
ISO 8601 string
Server timestamps. updatedAt moves every time sync writes new state.
Fees breakdown
Fees are categorized into different types based on their role in the order's lifecycle. Not every provider charges every type.
Liquidity
Fee applied by the liquidity provider to facilitate the swap.
Affiliate
Fee paid to the specified affiliate, projected from the API key config.
Service
SwapKit's service fee. Currently 0.
Network
Blockchain transaction fee for processing the order — destination-chain gas / outbound fee.
Each entry in fees[] has the following shape:
type
enum
liquidity, affiliate, service or network.
amount
string
Fee amount in human-readable decimal.
amountBps
number
Fee in basis points (100 bps = 1%).
asset
string
SwapKit asset identifier the fee is denominated in.
chain
string
Chain where the fee is paid or extracted.
protocol
enum
Provider that charges the fee.
Errors
404
limitOrderNotFound
Unknown orderId, or the order belongs to a different API key. Ownership is enforced — the response does not leak existence across keys.
401
apiKeyInvalid
Missing or unrecognized x-api-key header.
5. Paginated order list
Method: GET
URL: https://api.swapkit.dev/v3/limit/orders
Returns a paginated list of orders owned by the requesting API key, newest first. It does not re-sync with the provider on each call — use /v3/limit/orders/:orderId when freshness matters.
Query parameters
All optional. Unknown query keys are silently ignored. API-key scoping is always applied — you cannot see another partner's orders even by querying a known address.
sourceAddress
string
Filter by the blockchain address the asset is sold from (Order.maker). EVM addresses match case-insensitively, so checksummed and lowercase forms return the same orders. Non-EVM addresses (base58, bech32) match exactly, since their casing is significant.
destinationAddress
string
Filter by the recipient blockchain address (Order.receiver). Same casing rules as sourceAddress.
sourceChain
string enum
Filter by the sell-asset chain. Stringified EVM ids ("1", "42161", "8453", …) or a non-EVM slug ("bitcoin", "solana", "thorchain-1").
destinationChain
string enum
Filter by the buy-asset chain. Same value space as sourceChain.
Matches only orders whose destination chain was recorded when the order was created. Orders predating that column aren't backfilled, so an older order won't match this filter even when its buy asset is on the chain you asked for.
joinType
enum
How each filter pair combines internally — fullOuter (default) is OR, inner is AND. Applies to the address pair and the chain pair alike, and only matters when both halves of a pair are set.
status
enum or enum[]
Any of PENDING, SUBMITTED, OPEN, PARTIAL, FILLED, CANCELLED, EXPIRED, FAILED — see the order status lifecycle. Accepts a single value (?status=FILLED), a repeated key (?status=PENDING&status=FILLED), or a comma-separated list (?status=PENDING,FILLED).
cursor
ISO 8601 string
Opaque pagination cursor — echo back the nextCursor from the previous page. Don't parse or manufacture values; the server compares strictly (createdAt < cursor).
limit
int (1–100)
Default 50. Values outside the range return 400.
There is no provider filter — filter by sourceChain / destinationChain instead.
List response schema
orders
Order[]
Orders owned by the API key, sorted by createdAt descending. Order schema is explained above.
nextCursor
string (optional)
Pagination cursor for the next page. Present iff the page is full (orders.length === limit); its absence means the last page was reached.
To iterate: start with no cursor, then keep passing nextCursor until the response omits it.
Terminal states are opt-in. Omitting status does not return all statuses. The default is [PENDING, SUBMITTED, OPEN, PARTIAL] — active orders only. If you need terminal orders (FILLED / CANCELLED / EXPIRED / FAILED), list them explicitly, e.g. ?status=PENDING,SUBMITTED,OPEN,PARTIAL,FILLED,CANCELLED,EXPIRED,FAILED.
The chain pair is OR by default too, which is the surprising half: ?sourceChain=1&destinationChain=bitcoin returns every order touching Ethereum or Bitcoin on either side — not Ethereum→Bitcoin orders. Pass joinType=inner to require both halves.
The two axes are then AND'd with each other, and status on top of both.
Examples
Errors
400
validation_error
Bad limit (<1 or >100), unparseable cursor, or an invalid value in sourceChain / destinationChain / status / joinType. The body lists the accepted enum values.
401
apiKeyInvalid
Missing or unrecognized x-api-key header.
6. Cancel an order
Method: POST
URL: https://api.swapkit.dev/v3/limit/cancel/build
Force-syncs the order with the provider and returns the appropriate signing artifact for cancellation. One request shape; the artifact you get back depends on how the order's venue and sell chain expect a cancellation to be authorised.
Request schema
orderId
string
Yes
The ID of the order to cancel. Obtained from /v3/limit/build or a /v3/limit/orders response
Cancel build response schema
Exactly one of the three artifacts is non-null. Branch on which field is populated, not on the provider.
orderId
string
UUID of the order being cancelled.
cancelTx
object (optional)
Ready-to-broadcast cancel transaction, for venues that cancel on-chain. null otherwise. 1inch today.
typedData
object (optional)
Off-chain cancel payload for the wallet to sign, for venues that accept a signed cancellation. null otherwise. Harbor EVM today.
btcSignaturePreimage
string (optional)
Canonical-JSON string to sign as a Bitcoin Signed Message, for Bitcoin-side orders. null otherwise. Harbor BTC today.
nextActions
object[]
Data needed for the next request in the flow (/v3/limit/cancel/submit call).
The wallet broadcasts a cancel transaction to the venue's contract. Any existing token allowance is unaffected — only the specific order is invalidated. On 1inch this is a cancelOrder(makerTraits, orderHash) call on the aggregation router.
The wallet broadcasts it; you then post the resulting txHash to /v3/limit/cancel/submit.
The wallet signs an EIP-712 cancellation message. Nothing goes on-chain from your side. Harbor's EVM payload uses CancelAndWithdraw:
Sign the payload exactly as returned. Note that payload.orderId is the provider's internal order id, not the SwapKit orderId or orderHash — on Harbor it's the clientOrderId (trd-…). nonce and expiry are JSON numbers (uint256 wire-encoded as JS numbers; values stay below Number.MAX_SAFE_INTEGER).
The wallet signs the canonical-JSON string as a Bitcoin Signed Message (BIP-137).
Submit the base64-encoded compact secp256k1 signature.
Sign the preimage byte-for-byte. The btcSignaturePreimage string is canonical JSON — keys alphabetically sorted, no whitespace, applied recursively — and the signature is verified over exactly those bytes. Sign the string as returned rather than re-serialising the object.
Two field-naming traps if you do rebuild it: the Bitcoin payload uses snake_case l1_address where the EVM payload uses camelCase l1Address, and its domain is a flat string ("harbor.orderbook.trading") rather than the EVM nested { name, version, … } object.
Preconditions. /v3/limit/cancel/build force-syncs the order with the provider before responding. If the provider hasn't acknowledged the order yet, the call fails with limitOrderInvalidState — on Harbor, with the message "Harbor clientOrderId not yet known — wait until status reaches OPEN". Wait for the order to advance past SUBMITTED before attempting cancellation.
7. Submit the cancellation
Method: POST
URL: https://api.swapkit.dev/v3/limit/cancel/submit
Records the broadcast cancel transaction or the wallet's cancellation signature, depending on which artifact /v3/limit/cancel/build returned.
Request schema
orderId
string
All cancellations
The ID of the order being cancelled. Obtained from a previous /v3/limit/cancel/build response
txHash
string
cancelTx
Hash of the broadcast cancel transaction.
signature
string
typedData, btcSignaturePreimage
Wallet's cancellation signature — 0x-hex for an EIP-712 signature, base64 for a BIP-137 one. The provider verifies the format server-side.
Cancel submit response schema
orderId
string
UUID of the cancelled order.
txHash
string (optional)
Hash of the broadcast cancel transaction. Returned only for the cancelTx path; omitted for signatures.
Cancel payloads can expire. Signed cancel payloads carry a server-side expiry — Harbor's is set 10 minutes after /v3/limit/cancel/build. If the wallet takes longer than that to sign and you submit a stale payload, /v3/limit/cancel/submit returns limitOrderInvalidState with a message such as "cancel payload expired — call /v3/limit/cancel/build again". Read expiry off the payload rather than hard-coding the window, and have hardware-wallet flows re-build before signing if they're running close to it.
Errors
409
limitOrderInvalidState
The order is in a state that doesn't allow cancellation: a terminal state, the provider hasn't acknowledged it yet, the payload is past its expiry, or it's already cancelled. The body carries a descriptive message.
400
limitOrderCancelFailed
The provider rejected the cancellation.
404
limitOrderNotFound
Unknown orderId, or it belongs to a different API key.
Order status lifecycle
PENDING → SUBMITTED → OPEN → PARTIAL → FILLED / CANCELLED / EXPIRED / FAILED
Everything past SUBMITTED is driven by a poller that reconciles SUBMITTED / OPEN / PARTIAL orders against the provider every 30 minutes, publishing each transition to the webhook deliverer. GET /v3/limit/orders/:orderId force-syncs on every call — use it when you need state fresher than the poller interval.
PENDING
The order has been built but /v3/limit/submit hasn't been called yet. No signature or deposit hash on file.
SUBMITTED
/v3/limit/submit has attached the wallet signature or the deposit tx hash, but the provider hasn't acknowledged the order as resting yet (still in the mempool, or not yet indexed).
OPEN
Emitted by the poller once the provider confirms the order is resting. Not monotonic — if a provider temporarily has no record of an order, sync can regress it OPEN → SUBMITTED until visibility returns.
PARTIAL
The order has been partly filled. Only emitted by providers that support partial fills; all-or-nothing paths go straight to FILLED.
FILLED, CANCELLED, EXPIRED, FAILED
Terminal.
Auto-expiry of unsubmitted orders. At the start of every poller tick, SwapKit sweeps PENDING orders that never received a /v3/limit/submit (no signature, no deposit hash) and flips them to EXPIRED when either the order's own expiresAt has passed or it has been sitting unsubmitted for longer than the grace window (1 hour by default, configurable via LIMIT_ORDER_SUBMIT_GRACE_SECONDS). The sweep is provider-agnostic. Orders are marked EXPIRED rather than deleted so they remain in the audit history. Attempts to submit an already-expired order return limitOrderInvalidState.
Webhooks
Configure apiKey.settings.LIMIT_ORDER_WEBHOOK_URL (under ApiKeySettingsSchema, alongside NOTIFICATION and VAULT_SWAPS; apiKey.config is reserved strictly for fee configuration). The webhook deliverer POSTs every status transition over HTTP, for orders on any provider.
Verify authenticity via the x-swapkit-signature header, which carries an HMAC-SHA256 of the raw body keyed to the webhook secret.
Headers
x-swapkit-signature
Hex HMAC-SHA256 of the raw body, keyed to the webhook secret.
content-type
Always application/json.
x-swapkit-event-id
Matches eventId in the body. Dedupe on it.
x-swapkit-delivery-attempt
1-based delivery attempt number for this event.
Body schema
type
string
Always "limitOrder.statusChanged".
eventId
string
Unique id for this delivery event. Use it to dedupe redeliveries.
orderId
string
UUID of this specific order.
orderHash
string
Canonical on-chain identifier of the order, 0x-prefixed.
apiKeyId
number
Numeric id of the API key that owns the order.
provider
string
Provider that routed the order.
chainId
string
ChainId of the sell asset.
maker
string
Sell-side address that owns the order.
sellAsset, buyAsset
string
SwapKit asset identifiers.
sellAmount, buyAmount
decimal string
Order amounts, human-readable decimal.
limitPrice
decimal string
buyAsset per 1 sellAsset
previousStatus
enum
The status the order transitioned from.
previousFilledSellAmount, previousFilledBuyAmount
decimal string
Filled amounts before this transition.
filledSellAmount, filledBuyAmount
decimal string
Filled amounts after this transition.
integratorFeeBps
number or null
Integrator fee encoded on the order.
createdAt
ISO 8601 string
When the transition was recorded.
Integrator checklist
Obtain an API key configured for limit orders. The affiliate fee and integrator recipient live on the key's config — see Monetization.
Call
/v3/limit/quote. Pass the pair plus any two ofsellAmount/buyAmount/limitPrice. Optionally pre-sendsourceAddress/destinationAddressfor early screening.Inspect the route.
spotPriceDeviationBpsshows the user how far their limit is from spot;minExpirationSeconds/maxExpirationSecondsbound theexpiresAtyou can pass to/v3/limit/build.Call
/v3/limit/buildwithrouteId,sourceAddress,destinationAddress, andexpiresAt.Handle the token approval. Check
isApprovedand broadcast the accompanyingapprovalTxfirst if it'sfalse.Sign per the signing model. If
typedDatais non-null, the wallet signs it and you post{ orderId, signature }to/v3/limit/submit. Iftxis non-null, the wallet signs and broadcasts it — usingtxMeta.txTypeto pick the right codec — and you post{ orderId, depositTxHash }.Track state. Poll
GET /v3/limit/orders/:orderId(which also refreshes from the provider) or paginate viaGET /v3/limit/orders. TreatOPENas non-monotonic.Cancel when needed.
POST /v3/limit/cancel/build→ the wallet signs or broadcasts whichever artifact came back →POST /v3/limit/cancel/submit.Optional: webhooks. Configure
apiKey.settings.LIMIT_ORDER_WEBHOOK_URLto receivePENDING→SUBMITTED→OPEN→PARTIAL→FILLED/CANCELLED/EXPIREDtransitions over HTTP.
Last updated

