https://orchestration.flashnet.xyz
OpenAPI:
- Swagger UI:
GET /docs - OpenAPI JSON:
GET /openapi.json
GET /v1/orchestration/estimate.
This product is named Orchestra. The API path prefix is still /v1/orchestration/*.
Start here:
- Product overview: Orchestra
- End-to-end integration flow: Quickstart
Authentication
Authenticated endpoints require an API key:GET /v1/orchestration/estimate for unauthenticated price previews.
POST /v1/orchestration/quote is the start of a submit-able flow and should be called with Authorization. Quotes created without Authorization (if accepted) cannot be submitted.
A quote is bound to your partner account and can only be submitted by an API key for the same partner.
If a quote uses affiliateId, Authorization is required because affiliate profiles are partner-scoped.
Idempotency
Partner-authenticated mutating endpoints requireX-Idempotency-Key.
Rules:
- Key scope is
(partnerId, endpoint, key). - Replaying the same request returns the stored response and sets
X-Idempotency-Replayed: true. - Reusing a key with a different JSON body returns
409 idempotency_conflict.
Errors
Errors use a single envelope:error.code values:
unauthorized: Missing or invalid API keyauth_required: Authentication is required for this requestmissing_idempotency_key:X-Idempotency-Keymissingidempotency_conflict: Same idempotency key reused with a different payloadunsupported_route: Route is not supportedunsupported_amount_mode:amountModeis not supported for the selected routeunsupported_fee_plan:appFeesoraffiliateIdis not supported for the selected route or quote modeinvalid_request: Request body failed business validationinvalid_query: Invalid query parametersinvalid_address: Recipient or refund address is invalidinvalid_state: Endpoint was called in the wrong order stateconflict: State changed concurrently, retry the requestquote_expired: Quote has expirednot_found: Quote or order not foundamount_too_small: Swap input is below the Flashnet pool minimumamount_too_large: Amount is too large for the selected routerate_limited: Too many requestsflashnet_error: Swap simulation or execution failedprice_impact_too_high: Quote exceeds configured price impact constraintsinternal_error: Unhandled error
internal_error. Treat it as a request bug and retry only after fixing the payload.
Chains, Assets, Amounts
Routes are expressed as(sourceChain, sourceAsset) -> (destinationChain, destinationAsset).
Supported chains:
basesolanasparkbitcoinlightning
USDCUSDBBTCETH(Base)SOL(Solana)
Orchestra API
GET /v1/orchestration/estimate
Lightweight price preview. This does not create a quote and does not write to the database. Query parameters:| Param | Required | Notes |
|---|---|---|
sourceChain | Yes | One of the supported chains |
sourceAsset | Yes | One of the supported assets |
destinationChain | Yes | One of the supported chains |
destinationAsset | Yes | One of the supported assets |
amount | Yes | Integer string |
feeAsset is USDC for most routes and USDB for BTC -> USDB.
POST /v1/orchestration/quote
Request a durable quote with deposit instructions. Quotes expire 30 minutes after creation. Exact-in example (default mode):bitcoin:BTC -> base|solana:USDC only):
spark:BTC -> solana:USDC):
spark:BTC -> solana:USDC):
solana:SOL -> spark:BTC):
amountModedefaults toexact_in.amountMode=exact_outis currently supported only forbitcoin:BTC -> (base|solana):USDC.refundAddressis required whenamountMode=exact_out.- If
Authorizationis included, sendX-Idempotency-Key. appFeesandaffiliateIdare currently supported only foramountMode=exact_inand whensourceChainordestinationChainisbase|solana.- Fee settlement chain is
sourceChainwhen source isbase|solana, otherwisedestinationChain. appFeesmax length is16.- Each
appFees[i].feeis fee bps (1..10000). - Sum of all app fee bps must be
<= 10000. affiliateIdmust match^[a-z0-9][a-z0-9_-]{0,63}$after trimming and lowercasing.- Use either
appFeesoraffiliateId, not both. affiliateIdrequires authenticated quote requests.slippageBpsdefaults to50when omitted.zeroconfEnabledis only used whensourceChain = bitcoinandamountMode = exact_in.- If omitted, it defaults to
true. - If the deployment does not have ZeroConf configured, the engine falls back to on-chain confirmations.
- If omitted, it defaults to
- For
destinationChain = lightning,recipientAddressmust be a 0-amount (amountless) BOLT11 invoice.
- Inline recipients:
appFees: [{ recipient: string, fee: number }]feeis fee bps per recipient (1..10000).
- Registry by id:
affiliateId: string- profile is managed through
PUT /v1/affiliates/:affiliateId - quote resolves recipient + fee bps from your partner-scoped profile
- Settlement asset is always USDC.
- Settlement chain is selected as:
sourceChainwhen source isbase|solana- otherwise
destinationChain(must bebase|solana)
- Execution point:
- If source is
base|solana, fees are deducted on the source-side USDC leg before bridge/swap continuation. - If source is not
base|solana, fees are deducted on the destination-side USDC payout leg.
- If source is
- For source native routes like
SOL -> BTCandETH -> BTC, fees are still deducted on the source-side USDC leg after ingress conversion (SOL|ETH -> USDC). - Quote math order:
- platform fee (
feeAmount) - optional sweep fee (
sweepFeeAmount, USDC-source routes only) - app fee allocation (
appFeeAmount,appFees) - net amount proceeds to route execution
- platform fee (
appFeesrecipients are validated against the settlement-chain address format.- For
affiliateId, the profile must have a recipient address for the quote settlement chain (baseand/orsolana).
depositAddressdepends onsourceChain:base|solana: chain address that receives the source assetspark: Spark address that receives BTC or USDBbitcoin: Bitcoin L1 addresslightning: BOLT11 invoice to pay
totalFeeAmountis returned whenfeeAssetisUSDCand equalsfeeAmount + appFeeAmount + sweepFeeAmount(when present).sweepFeeAmountis returned on some USDC-source routes when a sweep fee is configured, and is included intotalFeeAmount.appFeeAmountandappFeesare returned whenappFeesoraffiliateIdwas requested.appFees[*].amountis quoted in settlement-chain USDC smallest units.appFees[*].affiliateIdis present when the quote usedaffiliateId.zeroconfEnabledis only present forsourceChain = bitcoin.lightningReceiveRequestIdis only present forsourceChain = lightningquotes.feeAssetisUSDBforBTC -> USDBquotes andUSDCotherwise.targetAmountOut,requiredAmountIn,maxAcceptedAmountIn, andinputBufferBpsare present for exact-out quotes.inputBufferBpsis currently2whenslippageBps=0, otherwise0.priceLockModeandlockedMinAmountOutare present for routes where a Flashnet min-out lock is enforced.
POST /v1/orchestration/submit
Create an order from a quote after you have initiated the source deposit. Headers:sourceChain.
Base/Solana sources (sourceChain = base|solana):
sourceAddress is optional but recommended. It is required when depositing to a shared address. When present, deposit verification requires the sender to match.
Spark sources (sourceChain = spark):
sourceSparkAddress is optional but recommended. It is required when depositing to a shared Spark address. When present, deposit verification requires the sender to match.
Bitcoin L1 sources (sourceChain = bitcoin):
sourceChain = lightning):
lightningReceiveRequestId can be omitted. When omitted, the API uses the value embedded in the quote.
Response:
(sourceChain, sourceTxHash[, sourceTxVout]), submit returns the existing { orderId, status }.
POST /v1/orchestration/reprice/approve
Approve a pending repricing request and resume execution. Headers:- Order must be in
awaiting_approval. approvedMinAmountOutmust be less than or equal to the previous locked minimum inorder.reprice.previousLockedMinAmountOut.
POST /v1/orchestration/reprice/reject
Reject repricing and fail the order. Headers:POST /v1/orchestration/reprice/refund
Reject repricing and request an on-chain BTC refund. Headers:- Order must be in
awaiting_approval. - Refund is currently supported only for Bitcoin source orders.
refundAddresscan come from the quote or this request; one must be present.
refunded or failed.
GET /v1/orchestration/status
Check an order’s current state. Authentication is optional:- Without
Authorization: returns a redacted order record. - With
Authorization: returns the full order record for your partner only.
| Param | Notes |
|---|---|
id | Order id (ord_...) |
quoteId | Quote id (q_...) |
txHash | Source transaction identifier (see order.sourceTxHash) |
txHash lookup returns the most recently created order with that sourceTxHash. For Bitcoin L1 deposits where multiple orders can share the same txid, prefer id from webhooks for exact attribution.
Possible order.status values:
processingconfirmingbridgingswappingawaiting_approvalrefundingdeliveringcompletedfailedrefunded
Authorization, the order object is the full public operation record. Without it, some fields are omitted. Depending on route and progress, the order can include:
flashnetRequestIdwhen a Flashnet swap has executedsparkTxHashwhen a Spark transfer, withdrawal, or Lightning action is createdswapmetadata when a Flashnet swap leg has been recordedpriceLock,reprice, andpaymentIntentmetadata for locked/approval-required flowsfeePlanwhen quote-levelappFeesoraffiliateIdwas requestedfeePayoutsas affiliate and recipient payout legs are executedfeePayouts.entries[*].roleisapp_feeorrecipient_payoutfeePayouts.entries[*].affiliateIdis present for app-fee entries derived fromaffiliateIdfeePayouts.entries[*].legisfull,instant, orholdbackfor routes that execute in multiple legs
GET /v1/orchestration/history
List your orders filtered byrecipientAddress.
Headers:
| Param | Required | Notes |
|---|---|---|
address | Yes | Recipient address (Spark address, Bitcoin address, Lightning invoice, or Base/Solana address depending on the route) |
status | No | Exact match on the order status |
limit | No | Default 50, max 200 |
offset | No | Default 0 |
GET /v1/orchestration/status, including optional swap, priceLock, reprice, and paymentIntent metadata when present.
Affiliates
Affiliate profiles let you register a partner-scoped app-fee recipient for Base and/or Solana under a stableaffiliateId, then reference that id in quote requests.
PUT /v1/affiliates/:affiliateId
Create or update an affiliate profile. Headers:settlementChain+recipientAddress
GET /v1/affiliates
List affiliate profiles for your partner. Headers:includeDisabled(true|false, defaultfalse)limit(default200, max1000)offset(default0)
DELETE /v1/affiliates/:affiliateId
Disable an affiliate profile. Headers:Partner Webhooks
Webhooks deliver order status changes for your partner account.POST /v1/webhooks
Register an endpoint. Headers:secret is only returned at creation time. Store it.
DELETE /v1/webhooks/:id
Disable an endpoint. Headers:Accumulation Addresses
Accumulation addresses are reusable deposit addresses on Base and Solana. Each deposit automatically creates an order that deliversBTC or USDB on Spark.
POST /v1/accumulation-addresses
Create an accumulation address. Headers:POST /v1/accumulation-addresses/sync
Re-sync all enabled accumulation addresses into the configured deposit detection webhooks. Headers:GET /v1/accumulation-addresses
List accumulation addresses.GET /v1/accumulation-addresses/:id
Get a single accumulation address, including subscription status.DELETE /v1/accumulation-addresses/:id
Disable an accumulation address. Headers:Liquidation Addresses
Liquidation addresses are reusable Bitcoin L1 deposit addresses. Each deposit automatically creates an order that delivers USDC on Base or Solana.POST /v1/liquidation-addresses
Create a liquidation address. Headers:GET /v1/liquidation-addresses
List enabled liquidation addresses.GET /v1/liquidation-addresses/:id
Get a single liquidation address (enabled or disabled).DELETE /v1/liquidation-addresses/:id
Disable a liquidation address. Headers:POST /v1/liquidation-addresses/sync-blockdaemon
Synchronize enabled liquidation addresses into Blockdaemon Streaming watchlists. Headers:Rate Limiting
Some public endpoints are IP rate-limited when rate limiting is enabled in the deployment:GET /v1/orchestration/estimate: 120 requests per minutePOST /v1/orchestration/quote: 60 requests per minuteGET /v1/orchestration/status: 120 requests per minute
X-RateLimit-* headers and the API returns 429 rate_limited on exhaustion.