Skip to main content

How do quotes become orders?

A quote is a priced intent with a 2-minute TTL. Call POST /v1/orchestration/quote to get deposit instructions and pricing. A quote does not create an order. An order is created when you call POST /v1/orchestration/submit with the quote ID and a funded deposit proof. The order tracks execution from deposit through delivery. Orders can also be created automatically via submissionless flows. When a deposit arrives at a quote’s deposit address, the system detects the deposit via webhooks (Helius for Solana, Blockdaemon for Bitcoin) or polling (Spark ingress scan) and creates the order without requiring an explicit /submit call. Late deposits (arriving after quote expiry) are always accepted and repriced at the live market rate at detection time; execution is still bounded by the quote’s slippageBps. For submissionless flows, use GET /v1/orchestration/order?quoteId=... to poll for order creation. This endpoint returns both the quote state and the order (or null if no deposit has been detected yet). See API: Quotes and Orders for the full response shape.
GET /v1/orchestration/status?quoteId=... returns 404 for quotes that have not been submitted. Use GET /v1/orchestration/order instead when you need to poll before the order exists.

Status state machine

Every order moves through a subset of these statuses. The allowed transitions from each status are:
awaiting_approval is used only for ZeroConf offer acceptance. Market-move refunds happen automatically from swapping to refunding without partner action.
paused is an internal operator-review state. Public status responses, SSE, and webhook payloads show paused orders as processing until the order resumes or moves to a partner-visible final state.

Typical happy paths

Stablecoin to BTC: processing -> bridging -> swapping -> delivering -> completed BTC to Stablecoin (Spark source): processing -> swapping -> bridging -> delivering -> completed BTC to Stablecoin (Bitcoin L1 source, no ZeroConf): processing -> confirming -> swapping -> bridging -> delivering -> completed BTC to Stablecoin (Bitcoin L1 source, ZeroConf offer): processing -> awaiting_approval -> processing -> swapping -> bridging -> delivering -> completed Refund on market move: processing -> swapping -> refunding -> refunded Lightning to direct BTC (Spark or Bitcoin L1): processing -> confirming -> delivering -> completed Terminal statuses are completed, failed, expired, unfulfilled, and refunded. unfulfilled is the one terminal status that can resume: a late deposit moves the order back through confirming, bridging, swapping, and delivering to completed. Treat it as settled for now and keep listening; webhooks fire when the order resumes. SSE streams close only on completed, failed, or refunded, and stay open through unfulfilled. Some unfulfilled orders are recovered instead by a new order rather than resuming in place — most often when a Bitcoin deposit is replaced (e.g. RBF’d) so the original transaction never confirms and the confirmed replacement is picked up separately. The original stays unfulfilled and links to its successor via supersededByOperationId (with a dedicated order.superseded webhook), while the successor carries recoveredFromOperationId. See Recovered (superseded) orders.
Not every status appears in every route. A Spark-source sell may skip confirming and bridging. Track progress via webhooks or poll GET /v1/orchestration/status rather than assuming a fixed sequence.

Field mapping

Fields shift names and semantics between the quote response and the order (status/webhook payload).
When the actual deposit differs from the quoted amount, the engine updates amountIn and feeAmount on the order before proceeding. An amount_reconciled stage is recorded. Webhook payloads and status responses reflect the updated values.

What happens if the deposit amount differs from the quote?

Deposits do not need to match the quoted amountIn exactly. Any positive deposit is accepted. The engine adjusts amountIn and fees to reflect the actual deposit before execution. Slippage protection is enforced at swap execution time against the quote’s slippageBps.
  • Underpayment: accepted for exact-in orders. Smaller deposits produce less price impact, so the per-unit rate stays the same or improves. Exact-out orders that receive less than requiredAmountIn refund automatically with exact_out_insufficient_input.
  • Overpayment: accepted for exact-in orders. Exact-out orders that receive more than maxAcceptedAmountIn refund automatically with exact_out_input_above_max.
  • Market moved past slippage: if the pool price shifts past slippageBps between deposit and execution, the order refunds automatically with slippage_exceeded. No partner action is required.
Lightning deposits are excluded from flexible amounts since invoices are fixed-amount by protocol design.

Market moves and refunds

Orders that cannot execute cleanly refund without partner involvement. The relevant errorCode values on the order record are:
  • slippage_exceeded: pool moved past slippageBps between deposit and execution
  • exact_out_insufficient_input: exact-out deposit below requiredAmountIn
  • exact_out_input_above_max: exact-out deposit above maxAcceptedAmountIn
  • exact_out_target_not_met: pool couldn’t produce targetAmountOut at execution time
All refund paths emit order.refunding followed by order.refunded. Partners should render these as normal refund outcomes. There is no accept/decline endpoint. Once the deposit lands, execution proceeds automatically or refunds. See Error codes for the full list.

What are the effective limits?

Order size limits combine several constraint types:
  • Per-direction USD notional bounds, tuned by operators at runtime. These change without notice, so do not hardcode them.
  • Per-route minimums for specific destinations.
  • Provider limits applied at quote time on bridged routes.
  • Static BTC minimums enforced in code: 5,000 sats swap input for Bitcoin L1 deposits, 1,200 sats swap input for Spark deposits, and 10,000 sats for Bitcoin L1 delivery.
  • An amountFiatUsd band of 1.00to1.00 to 50,000.00 on onramp and pay links.
Call GET /v1/orchestration/limits for the current limits on any route. The endpoint is public and authoritative; query it instead of hardcoding numbers.
Some USDC-source routes include a sweep fee (sweepFeeAmount in the quote response). The sweep fee is deducted from the input before execution, so it raises the effective minimum above the published route minimum. Check the quote response for sweepFeeAmount on affected routes.
For lightning:BTC -> bitcoin:BTC, the final on-chain withdrawal also pays the network withdrawal fee quoted at delivery time. That fee is separate from the platform fee shown on the quote or order.

Next steps