# Create accumulation address
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/create-accumulation-address
https://orchestration.flashnet.xyz/openapi.json POST /v1/accumulation-addresses
Legacy address model; new integrations use standing deposit addresses. Create accumulation address.
# Delete accumulation address
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/delete-accumulation-address
https://orchestration.flashnet.xyz/openapi.json DELETE /v1/accumulation-addresses/{id}
Legacy address model; new integrations use standing deposit addresses. Delete accumulation address.
# Get accumulation address
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/get-accumulation-address
https://orchestration.flashnet.xyz/openapi.json GET /v1/accumulation-addresses/{id}
Legacy address model; new integrations use standing deposit addresses. Get accumulation address.
# List accumulation addresses
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/list-accumulation-addresses
https://orchestration.flashnet.xyz/openapi.json GET /v1/accumulation-addresses
Legacy address model; new integrations use standing deposit addresses. List accumulation addresses.
# Reindex accumulation address
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/reindex-accumulation-address
https://orchestration.flashnet.xyz/openapi.json POST /v1/accumulation-addresses/{idOrAddress}/reindex
Legacy address model; new integrations use standing deposit addresses. Reindex accumulation address.
# Sync accumulation webhooks
Source: https://docs.flashnet.xyz/api-reference/accumulation-addresses/sync-accumulation-webhooks
https://orchestration.flashnet.xyz/openapi.json POST /v1/accumulation-addresses/sync
Legacy address model; new integrations use standing deposit addresses. Sync accumulation webhooks.
# Claim affiliate balance
Source: https://docs.flashnet.xyz/api-reference/affiliates/claim-affiliate-balance
https://orchestration.flashnet.xyz/openapi.json POST /v1/affiliates/{affiliateId}/claim
Claim the full accrued balance.
# Disable affiliate
Source: https://docs.flashnet.xyz/api-reference/affiliates/disable-affiliate
https://orchestration.flashnet.xyz/openapi.json DELETE /v1/affiliates/{affiliateId}
Disable an affiliate.
# Get affiliate claim
Source: https://docs.flashnet.xyz/api-reference/affiliates/get-affiliate-claim
https://orchestration.flashnet.xyz/openapi.json GET /v1/affiliates/{affiliateId}/claims/{id}
One claim.
# List affiliate claims
Source: https://docs.flashnet.xyz/api-reference/affiliates/list-affiliate-claims
https://orchestration.flashnet.xyz/openapi.json GET /v1/affiliates/{affiliateId}/claims
Claims for an affiliate.
# List affiliates
Source: https://docs.flashnet.xyz/api-reference/affiliates/list-affiliates
https://orchestration.flashnet.xyz/openapi.json GET /v1/affiliates
Affiliate profiles.
# Upsert affiliate
Source: https://docs.flashnet.xyz/api-reference/affiliates/upsert-affiliate
https://orchestration.flashnet.xyz/openapi.json PUT /v1/affiliates/{affiliateId}
Create or update an affiliate profile and payout destination.
# Create liquidation address
Source: https://docs.flashnet.xyz/api-reference/liquidations/create-liquidation-address
https://orchestration.flashnet.xyz/openapi.json POST /v1/liquidation-addresses
Legacy address model; new integrations use standing deposit addresses. Create liquidation address.
# Delete liquidation address
Source: https://docs.flashnet.xyz/api-reference/liquidations/delete-liquidation-address
https://orchestration.flashnet.xyz/openapi.json DELETE /v1/liquidation-addresses/{id}
Legacy address model; new integrations use standing deposit addresses. Delete liquidation address.
# Get liquidation address
Source: https://docs.flashnet.xyz/api-reference/liquidations/get-liquidation-address
https://orchestration.flashnet.xyz/openapi.json GET /v1/liquidation-addresses/{id}
Legacy address model; new integrations use standing deposit addresses. Get liquidation address.
# List liquidation addresses
Source: https://docs.flashnet.xyz/api-reference/liquidations/list-liquidation-addresses
https://orchestration.flashnet.xyz/openapi.json GET /v1/liquidation-addresses
Legacy address model; new integrations use standing deposit addresses. List liquidation addresses.
# List liquidation orders
Source: https://docs.flashnet.xyz/api-reference/liquidations/list-liquidation-orders
https://orchestration.flashnet.xyz/openapi.json GET /v1/liquidation-addresses/orders
Legacy address model; new integrations use standing deposit addresses. List liquidation orders.
# Accept ZeroConf offer
Source: https://docs.flashnet.xyz/api-reference/orchestration/accept-zeroconf-offer
https://orchestration.flashnet.xyz/openapi.json POST /v1/orchestration/zeroconf/accept
Accept a pending ZeroConf offer and credit the deposit before confirmation.
# Create onramp order
Source: https://docs.flashnet.xyz/api-reference/orchestration/create-onramp-order
https://orchestration.flashnet.xyz/openapi.json POST /v1/orchestration/onramp
Quote and order in one call for a Lightning payment into any destination.
# Create quote
Source: https://docs.flashnet.xyz/api-reference/orchestration/create-quote
https://orchestration.flashnet.xyz/openapi.json POST /v1/orchestration/quote
Firm price for 2 minutes plus a deposit address. Paying the address creates the order.
# Decline ZeroConf offer
Source: https://docs.flashnet.xyz/api-reference/orchestration/decline-zeroconf-offer
https://orchestration.flashnet.xyz/openapi.json POST /v1/orchestration/zeroconf/decline
Decline a pending ZeroConf offer and wait for one confirmation.
# Estimate a swap
Source: https://docs.flashnet.xyz/api-reference/orchestration/estimate-a-swap
https://orchestration.flashnet.xyz/openapi.json GET /v1/orchestration/estimate
Indicative price for a route, no state created. Public.
# Get order status
Source: https://docs.flashnet.xyz/api-reference/orchestration/get-order-status
https://orchestration.flashnet.xyz/openapi.json GET /v1/orchestration/status
Authenticated order state by exactly one of id, quoteId, or txHash; client keys require id or quoteId with a matching read token.
# Get quote and order
Source: https://docs.flashnet.xyz/api-reference/orchestration/get-quote-and-order
https://orchestration.flashnet.xyz/openapi.json GET /v1/orchestration/order
Quote and its order, or null while the deposit is awaited. Server key.
# Get route limits
Source: https://docs.flashnet.xyz/api-reference/orchestration/get-route-limits
https://orchestration.flashnet.xyz/openapi.json GET /v1/orchestration/limits
Amount bounds per route; pass filters to narrow results. Public.
# List order history
Source: https://docs.flashnet.xyz/api-reference/orchestration/list-order-history
https://orchestration.flashnet.xyz/openapi.json GET /v1/orchestration/history
Orders for a recipient address. Server key.
# List routes
Source: https://docs.flashnet.xyz/api-reference/orchestration/list-routes
https://orchestration.flashnet.xyz/openapi.json GET /v2/orchestration/routes
Every asset and the destinations it can reach. Public.
# Submit deposit proof
Source: https://docs.flashnet.xyz/api-reference/orchestration/submit-deposit-proof
https://orchestration.flashnet.xyz/openapi.json POST /v1/orchestration/submit
Attach a deposit transaction to a quote when automatic detection missed it. Returns the existing order if one exists.
# Accept deposit ZeroConf offer
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/accept-deposit-zeroconf-offer
https://orchestration.flashnet.xyz/openapi.json POST /v1/standing-deposit-addresses/{ref}/deposits/{depositId}/zeroconf/accept
Record the Bitcoin deposit ZeroConf decision; conflicting or stale decisions return standing_zeroconf_offer_conflict. Requires a server key.
# Decline deposit ZeroConf offer
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/decline-deposit-zeroconf-offer
https://orchestration.flashnet.xyz/openapi.json POST /v1/standing-deposit-addresses/{ref}/deposits/{depositId}/zeroconf/decline
Record the Bitcoin deposit ZeroConf decision; conflicting or stale decisions return standing_zeroconf_offer_conflict. Requires a server key.
# Get standing address
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/get-standing-address
https://orchestration.flashnet.xyz/openapi.json GET /v1/standing-deposit-addresses/{ref}
Addresses and enabled state for a ref. Requires a server key.
# List deposits
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/list-deposits
https://orchestration.flashnet.xyz/openapi.json GET /v1/standing-deposit-addresses/{ref}/deposits
Deposits observed on a ref, newest first. Requires a server key.
# Pause or resume conversion
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/pause-or-resume-conversion
https://orchestration.flashnet.xyz/openapi.json PATCH /v1/standing-deposit-addresses/{ref}
Pause or resume conversion while observation continues. Requires a server key.
# Register standing address
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/register-standing-address
https://orchestration.flashnet.xyz/openapi.json PUT /v1/standing-deposit-addresses/{ref}
Create or fetch permanent addresses for an immutable instruction. Requires a server key.
# Request deposit refund
Source: https://docs.flashnet.xyz/api-reference/standing-deposit-addresses/request-deposit-refund
https://orchestration.flashnet.xyz/openapi.json POST /v1/standing-deposit-addresses/{ref}/resolve
Return held deposits to a partner-supplied refund address after asynchronous eligibility checks. Requires a server key.
# Delete recovery webhook
Source: https://docs.flashnet.xyz/api-reference/webhooks/delete-recovery-webhook
https://orchestration.flashnet.xyz/openapi.json DELETE /v1/webhooks/recovery/{id}
Disable a recovery endpoint.
# Delete webhook
Source: https://docs.flashnet.xyz/api-reference/webhooks/delete-webhook
https://orchestration.flashnet.xyz/openapi.json DELETE /v1/webhooks/{id}
Disable an endpoint.
# List recovery webhooks
Source: https://docs.flashnet.xyz/api-reference/webhooks/list-recovery-webhooks
https://orchestration.flashnet.xyz/openapi.json GET /v1/webhooks/recovery
Registered recovery endpoints.
# List webhooks
Source: https://docs.flashnet.xyz/api-reference/webhooks/list-webhooks
https://orchestration.flashnet.xyz/openapi.json GET /v1/webhooks
Registered endpoints.
# Register recovery webhook
Source: https://docs.flashnet.xyz/api-reference/webhooks/register-recovery-webhook
https://orchestration.flashnet.xyz/openapi.json POST /v1/webhooks/recovery
Register an endpoint for order.recovery_required events.
# Register webhook
Source: https://docs.flashnet.xyz/api-reference/webhooks/register-webhook
https://orchestration.flashnet.xyz/openapi.json POST /v1/webhooks
Register an endpoint. The signing secret is returned once.
# Authentication
Source: https://docs.flashnet.xyz/api/authentication
Keys and read tokens
API keys authorize requests within a partner account. Send `Authorization: Bearer `; [SSE](/api/sse) uses a query parameter.
## Key types
Server keys (`fn_`) are backend secrets. They access partner endpoints, including history, webhooks, affiliate management and claims, and standing addresses.
Client keys (`fnp_`) can run in browsers and mobile apps. Scopes restrict their access; history, webhook management, and affiliate management and claims require server keys. Affiliate reporting permits a client key pinned to that affiliate with `affiliates:read`. See [Rate limits](/api/rate-limits) for defaults and partner overrides.
Find keys under API Keys in the [dashboard](https://orchestra.flashnet.xyz/dashboard). When account approval is enforced, pending or rejected accounts receive a 403.
## Scopes
Client keys carry scopes. A request outside the key's scopes returns `403 scope_required`; a request to an endpoint client keys can never reach returns `403 forbidden`.
* `orders:quote`: create quotes.
* `orders:submit`: submit orders.
* `orders:onramp`: create Lightning onramps.
* `orders:read`: read status.
* `orders:sse`: stream status.
* `accumulation:create`: create or reindex legacy accumulation addresses.
* `liquidation:create`: create legacy liquidation addresses.
* `affiliates:read`: read the pinned affiliate's reporting.
## Origin rules
Each client key has a mode. `server` ignores the `Origin` header. `browser` requires `Origin` and it must match the key's allowed origins, else `403 origin_required` or `403 origin_not_allowed`. `both` checks `Origin` only when present. An empty allowed-origins list accepts any origin. Global CORS applies on top.
## Read-tokens
Client reads also require a `readToken`, bound to the partner, issuing key, and one identifier for 24 hours:
* `/quote` returns a quote-bound token. Poll `/status?quoteId=QUOTE_ID` with it.
* `/submit` and `/onramp` return order-bound tokens. Use the matching `id` for status or SSE.
Status accepts `X-Read-Token` or `?readToken=`; direct client SSE accepts only the query parameter and requires an order-bound token. A quote token cannot authorize an order-ID stream. Keep polling by quote ID or use an authorized backend proxy. Server-key reads do not require a read token.
Missing tokens return `403 read_token_required`; expired or mismatched tokens return `403 invalid_read_token`.
## Other rules
* Field restrictions are endpoint-specific. Quote, onramp, and accumulation creation reject forbidden client fee fields with `403 client_key_fee_fields_forbidden`; other privileged fields may be stripped. Follow each endpoint's schema.
* When browser verification is enabled for submit or onramp, send the Turnstile token in `cf-turnstile-response`. Missing or rejected tokens return `403 turnstile_required` or `403 turnstile_failed`.
* A quote is bound to the partner that created it. Only a key from the same partner can submit it. Quotes created without `Authorization` cannot be submitted.
* Disable or revoke keys in the dashboard.
## Examples
Quote from your backend with a server key:
```bash theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"sourceChain": "base",
"sourceAsset": "USDC",
"destinationChain": "spark",
"destinationAsset": "BTC",
"amount": "50000000",
"recipientAddress": "RECIPIENT_ADDRESS"
}'
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
The same quote from a browser with a client key in `browser` mode; the response includes `readToken`:
```bash theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
-H "Authorization: Bearer CLIENT_KEY" \
-H "Origin: https://app.example.com" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"sourceChain": "base",
"sourceAsset": "USDC",
"destinationChain": "spark",
"destinationAsset": "BTC",
"amount": "50000000",
"recipientAddress": "RECIPIENT_ADDRESS"
}'
```
# Errors
Source: https://docs.flashnet.xyz/api/errors
Request and order failures
API errors normally return `{"error":{"code":"...","message":"..."}}`. This page covers common integration errors, not every endpoint-specific code. [Attestation](/orchestra/security/attestation) uses a separate response shape.
## Authentication
* `unauthorized`, `auth_required` (401): missing or invalid credentials.
* `forbidden` (403): key cannot access the endpoint or resource.
* `partner_disabled` (403): disabled account.
* `account_pending_approval`, `account_rejected` (403): account approval required when enforcement is enabled.
* `scope_required` (403): missing client scope.
* `origin_required`, `origin_not_allowed` (403): missing or disallowed browser origin.
* `read_token_required` (403): missing client read token.
* `invalid_read_token` (403): expired token or wrong key, order, or quote.
* `client_key_fee_fields_forbidden` (403): client request includes server-only fee fields.
* `turnstile_required`, `turnstile_failed` (403): missing or rejected browser verification, when enabled.
See [Authentication](/api/authentication) for token binding and endpoint restrictions.
## Idempotency
* `missing_idempotency_key` (400): missing `X-Idempotency-Key`.
* `invalid_idempotency_key` (400): key outside 1 to 255 printable ASCII characters.
* `idempotency_conflict` (409): same key, different body.
* `idempotency_in_progress` (409): original request still executing; wait for `Retry-After`.
## Request validation
* `invalid_request`, `validation_error` (400): invalid request fields.
* `invalid_query` (400): invalid query, such as status without exactly one selector.
* `invalid_address` (400): recipient or refund address invalid for its chain.
* `invalid_tx_hash` (400): invalid source-chain transaction hash.
* `ambiguous_query` (400): transaction hash matches multiple orders.
* `invalid_payout_destination`, `invalid_payout_address` (400): unsupported payout route or invalid address.
* `invalid_webhook_url` (400): rejected webhook URL.
* `not_found` (404): missing resource or different partner's resource.
* `payload_too_large` (413): body exceeds 1 MiB.
* `unsupported_media_type` (415): body is not JSON.
## Routing and amounts
These errors return HTTP 400:
* `unsupported_route`: destination outside the source's `route.to`.
* `unsupported_amount_mode`: exact-out route outside `exactOutTo`.
* `unsupported_delivery_mode`: fixed delivery with exact-out.
* `fixed_delivery_unavailable`: fixed delivery unavailable for route or partner.
* `unsupported_fee_plan`: route does not support fee settlement.
* `unsupported_asset`: USD exact-out target uses a non-USD-stablecoin destination.
* `invoice_required`: USD exact-out requires invoice billing.
* `amount_too_small`, `amount_too_large`: amount outside route bounds.
* `price_impact_too_high`: price impact exceeds the cap.
* `route_disabled`: route turned off.
## State
* `invalid_state` (400): action does not apply to current state.
* `conflict` (409): conflicting resource or offer decision.
* `expired` (409): ZeroConf offer expired.
* `quote_expired` (409): Lightning destination invoice expired.
## Rate and availability
* `rate_limited` (429): exhausted bucket. Follow `Retry-After` when present, otherwise `X-RateLimit-Reset`; see [Rate limits](/api/rate-limits).
* `internal_error` (500): server error.
* `flashnet_error` (502): upstream failure.
* `service_unavailable` (503): unavailable service.
* `spot_unavailable` (503): stale BTC/USD spot; USD amount requests cannot proceed.
## Standing deposit addresses
* `invalid_destination` (400): unsupported standing destination.
* `immutable_instruction` (400): PATCH changes fields other than `enabled`.
* `instruction_conflict` (409): existing reference has different instructions.
* `refund_not_available` (409): deposit or batch cannot be refunded.
* `standing_zeroconf_offer_conflict` (409): conflicting, expired, or ineligible offer decision.
* `standing_unavailable`, `scanner_unavailable` (503): standing service or scanner unavailable.
## Order errors
After creation, failures and refunds carry `error.code` on the order:
* `slippage_exceeded`: execution outside `slippageBps`.
* `exact_out_insufficient_input`: deposit below `requiredAmountIn`.
* `exact_out_input_above_max`: deposit above `maxAcceptedAmountIn`.
* `exact_out_target_not_met`: execution could not reach `targetAmountOut`.
* `duplicate_lightning_invoice`: invoice already used.
* `refund_address_missing`: no refund target; operator recovery required.
Screening can hold an order for review. Neither `awaiting_approval` nor a recovery event implies a partner-actionable ZeroConf offer; inspect the [order snapshot](/orchestra/status).
# API overview
Source: https://docs.flashnet.xyz/api/overview
Request conventions
The Orchestra API accepts JSON requests at `https://orchestration.flashnet.xyz`.
## Conventions
* Request bodies are JSON. Any other content type returns `415 unsupported_media_type`.
* Bodies are capped at 1 MiB. Larger requests return `413 payload_too_large`.
* API errors normally return `{ "error": { "code": "...", "message": "..." } }`; [attestation](/orchestra/security/attestation) has a separate response shape. See [Errors](/api/errors).
* Amounts are integer strings in the asset's smallest unit.
* This reference is generated from the official [OpenAPI 3.1 document](https://orchestration.flashnet.xyz/openapi.json). [Swagger UI](https://orchestration.flashnet.xyz/docs) is also available.
## Idempotency
Every mutating endpoint called with a partner key requires an `X-Idempotency-Key` header of 1 to 255 printable ASCII characters. A UUID works. Server-key requests are scoped to `(partnerId, endpoint, key)`, so the same string on two endpoints is two keys. Client requests also include the API-key ID and caller IP in the scope; changing either does not preserve the same replay guarantee.
Replaying a request with the same key and body returns the stored response with `X-Idempotency-Replayed: true`. The same key with a different body returns `409 idempotency_conflict`. A replay while the original is still executing returns `409 idempotency_in_progress` with a `Retry-After` header; wait and retry with the same key.
Reuse the key after a timeout or 5xx response. Handler-produced 4xx responses are cached: after a definitive rejection, correcting the request or retrying with newly available deposit evidence is a new attempt and needs a new key. Keep the original funding transaction; do not send funds again.
## Endpoint groups
* [Quotes](/orchestra/quotes) and [orders](/orchestra/status): `/v1/orchestration/`. Route discovery uses `/v2/orchestration/routes`.
* [Standing deposit addresses](/orchestra/deposit-addresses): `/v1/standing-deposit-addresses/`.
* [Webhooks](/api/webhook-events): `/v1/webhooks`.
* [Affiliates](/orchestra/fees): `/v1/affiliates/`.
* [Pay links](/api/pay-links): `/v1/pay-links` and `/pay/`.
* [SSE](/api/sse): `/v1/sse/operations/`.
* [Legacy addresses](/orchestra/legacy/reusable-addresses): `/v1/accumulation-addresses` and `/v1/liquidation-addresses`, superseded by standing addresses.
Pay links and SSE have prose references; they are not in the OpenAPI document.
Authentication, key types, and scopes are on [Authentication](/api/authentication). Per-endpoint request quotas are on [Rate limits](/api/rate-limits).
# Pay links API
Source: https://docs.flashnet.xyz/api/pay-links
Create and manage checkout
A pay link stores payment terms for repeat Lightning checkout through Cash App. These endpoints are not in the OpenAPI spec; this page is the reference. See [Pay links](/orchestra/pay-links) for the flow.
## Endpoints
Management requires a server key; `POST` and `DELETE` require `X-Idempotency-Key`.
* `POST /v1/pay-links`: `201 { payLink }`.
* `GET /v1/pay-links`: `{ payLinks }`. `limit` defaults to 200, clamps to 1..1000; `offset` defaults to 0. Use `includeDisabled=true` to include disabled links.
* `GET /v1/pay-links/:id`: `{ payLink }`.
* `DELETE /v1/pay-links/:id`: `{ ok: true }`; disables the URL without cancelling existing orders.
Public `GET /pay/:shortId` serves HTML: mobile handoff or desktop navigation to `/go?n=...`. `/go` returns a mobile 302 to Cash App or desktop QR HTML. The same completed nonce reuses its order and invoice. These links do not extend invoice expiry. Onramp short URLs reuse one order and do not support `/go`.
## Request body
* `destinationChain`, `destinationAsset`: required. Assets: `USDB`, `USDC`, `USDT`, `PathUSD`. Chains: Spark, Solana, configured CCTP-forward destinations, excluding Hedera. The pair must have a configured `lightning:BTC` route.
* `recipientAddress`: required destination address.
* Exactly one of `amountOut` or `amountFiatUsd`; [amount modes](/orchestra/pay-links#amount-modes) determine fees and delivery. `amountOut` is a positive integer string in destination smallest units representing whole cents; fractional cents return `400 invalid_request`. `amountFiatUsd` is `"1.00"` to `"50000.00"`.
* Optional `affiliateId`: registered affiliate. Optional `label`: up to 255 characters, returned as webhook `payLinkLabel`.
Fiat-capped exact-out requires an eligible USD-stablecoin route, a positive partner-invoiced platform fee, and no app or affiliate fee plan. Other fiat links use exact-in.
## Example
Create a link targeting 25 USDC on Base:
```bash theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/pay-links" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"destinationChain": "base",
"destinationAsset": "USDC",
"recipientAddress": "RECIPIENT_ADDRESS",
"amountOut": "25000000",
"label": "Coffee fund"
}'
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Response excerpt:
```json theme={null}
{
"payLink": {
"id": "pl_01j7qn4a2b3c4d5e6f7g8h9j0k",
"shortId": "k3f9x2",
"destinationChain": "base",
"destinationAsset": "USDC",
"recipientAddress": "RECIPIENT_ADDRESS",
"amountOut": "25000000",
"label": "Coffee fund",
"shortUrl": "https://orchestration.flashnet.xyz/pay/k3f9x2"
}
}
```
Orders carry `payLinkId` and `payLinkLabel` in webhooks. History has no pay-link filter; query by `recipientAddress` and match webhook `payLinkId`.
# Rate limits
Source: https://docs.flashnet.xyz/api/rate-limits
Quotas and retry timing
Rate limits meter requests by IP, API key, or key and IP. Exhaustion returns `429 rate_limited`.
## Retry timing
IP limiters send `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset`, but no `Retry-After`. Key limiters send these headers only on denial, with `Retry-After` in seconds. Reset is an absolute Unix timestamp in milliseconds. Wait for `Retry-After` when present; otherwise use the reset timestamp.
## IP defaults
Limits below are per minute. Orchestration names refer to `/v1/orchestration/`, except the v2 route catalog.
* v2 routes and limits: 60 requests/IP.
* Unauthenticated estimate and authenticated order lookup: 120 requests/IP.
* SSE connection attempts, affiliate claims, and attestation: 30 requests/IP.
Status requires authentication; it has no anonymous polling mode.
## Key defaults
Client keys use two buckets. Values below are requests per minute, per key and per key+IP respectively. Partner overrides can change defaults and apply key limits to server keys.
* Quote: 600 / 60.
* Served estimate and status: 6000 / 1200.
* Submit and onramp: 120 / 10.
* Accumulation create/reindex and liquidation create: 60 / 5.
Authenticated estimates use the served-estimate bucket; legacy pricing can also charge the quote-budget limits.
Poll no faster than every 3 seconds per order. Use [webhooks or SSE](/orchestra/status) for updates; [contact support](mailto:support@flashnet.xyz) for higher quotas.
# SSE
Source: https://docs.flashnet.xyz/api/sse
Stream order status
`GET /v1/sse/operations/:id` streams status changes for one order as server-sent events. It is not in the OpenAPI spec; this page is the reference.
## Request
The key goes in the `token` query parameter. Server keys need no read token. Client keys need `orders:sse` and an order-bound `readToken` from `/submit` or `/onramp`, issued to the same key and matching `ORDER_ID`:
```
GET /v1/sse/operations/ORDER_ID?token=SERVER_KEY
GET /v1/sse/operations/ORDER_ID?token=CLIENT_KEY&readToken=READ_TOKEN
```
An order that does not exist or belongs to another partner returns `404 not_found`. The limit is 30 connections per minute per IP.
Direct client SSE accepts `readToken` only in the query. A `/quote` token is bound to the quote, not the order ID: keep polling status by `quoteId` or stream through your backend.
## Events
A `status` frame carries the current state on connect, followed by live updates. A `heartbeat` frame arrives every 15 seconds:
```
event: status
data: {"status":"swapping"}
event: heartbeat
data:
```
`paused` orders stream as `processing`. Reconnecting reads current state; it does not replay missed transitions or support `Last-Event-ID` recovery. Reconcile through the status API.
## Close
The server closes the stream after `completed`, `failed`, or `refunded`. It stays open on `unfulfilled`, because a late deposit can resume the order.
## Browsers
Keep server keys on your backend. Authorize the caller's access to the order before proxying the stream with `Content-Type: text/event-stream`. The browser connects to your proxy:
```javascript theme={null}
const stream = new EventSource(`/api/orders/${orderId}/events`);
stream.addEventListener("status", (e) => {
const { status } = JSON.parse(e.data);
render(status);
if (["completed", "failed", "refunded"].includes(status)) stream.close();
});
```
`EventSource` reconnects automatically. Fall back to authenticated status polling every 3 seconds if streaming remains unavailable.
# Webhook events
Source: https://docs.flashnet.xyz/api/webhook-events
Events and payloads
Webhooks deliver order snapshots for selected lifecycle events. Registration, signatures, retries, and deduplication are on [Order status](/orchestra/status).
## Events
* `order.processing`: processing started or resumed.
* `order.confirming`: awaiting source confirmations.
* `order.bridging`: crossing chains.
* `order.swapping`: swap executing.
* `order.awaiting_approval`: pending offer or review.
* `order.refunding`: refund underway.
* `order.delivering`: delivery underway.
* `order.completed`: delivered.
* `order.failed`: failure; inspect `data.error`.
* `order.unfulfilled`: deposit replaced or unconfirmed.
* `order.refunded`: refund confirmed.
* `order.superseded`: successor link advanced.
* `order.recovery_required`: operator release or refund decision needed.
Recovery events go only to `/v1/webhooks/recovery` subscriptions. Their data can carry `status: "processing"` with `reviewStatus` of `pending_review` or `blocked_wallet`. A review hold is not a ZeroConf offer.
Ordinary `paused`, `expired`, and Lightning deposit-timeout transitions emit no webhook. State normalization to `processing` does not imply an event. Reconcile through status polling.
## Envelope
```json theme={null}
{ "event": "order.completed", "timestamp": "2026-09-10T18:04:12.318Z", "data": { } }
```
## Payload fields
`data` is the event-time order snapshot. Amounts are integer strings in smallest units; timestamps are ISO 8601. This example shows selected fields, not a complete schema. Optional fields may be omitted rather than null.
An `order.completed` event for 50 USDC on Base delivered as BTC on Spark:
```json theme={null}
{
"event": "order.completed",
"timestamp": "2026-09-10T18:04:12.318Z",
"data": {
"id": "ord_01j7qm3x8k9v2n4p6r8t0w2y4a",
"type": "order",
"status": "completed",
"quoteId": "q_01j7qm2r5d7f9h1k3m5p7s9v1x",
"amountIn": "50000000",
"amountOut": "45120",
"feeBps": 5,
"feeAmount": "25000",
"slippageBps": 50,
"source": {
"chain": "base",
"asset": "USDC",
"address": "0x3f1b9c2d4e5f60718293a4b5c6d7e8f901234567",
"txHash": "0x9a8b7c6d5e4f30211203a4b5c6d7e8f9012345678901234567890abcdef12345",
"sweepTxHash": null
},
"destination": {
"chain": "spark",
"asset": "BTC",
"address": "sp1pgss9x2k4m6n8q0s2u4w6y8a0c2e4g6i8k0m2o4q6s8u0w2y4a6c8e0g2i4k",
"txHash": "5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"
},
"depositAddress": "0x7d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e",
"recipientAddress": "sp1pgss9x2k4m6n8q0s2u4w6y8a0c2e4g6i8k0m2o4q6s8u0w2y4a6c8e0g2i4k",
"flashnetRequestId": "req_01j7qm3y1a2b3c4d5e6f7g8h9j",
"sparkTxHash": "5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d",
"refund": { "asset": null, "amount": null, "txHash": null },
"error": null,
"createdAt": "2026-09-10T18:02:31.004Z",
"updatedAt": "2026-09-10T18:04:12.318Z",
"completedAt": "2026-09-10T18:04:12.318Z"
}
}
```
Notes on the fields that need them:
* `amountOut` is null before completion. `destination.txHash` records delivery; `refund` is always an object with nullable fields.
* `feeAmount` is in `feeAsset` units and reads "0" for invoice-billed partners.
* `quoteId` is null for legacy address deposits.
* `feePlan` and `feePayouts` are present when the order carries app or affiliate fees. Each payout entry has a role (app\_fee, recipient\_payout, platform\_fee, or fee\_custody) and a leg (full or holdback; instant appears only on historical multi-leg records).
* `amountFiatUsd`, `amountFiatCurrency`, and `spotUsdPerBtc` appear on orders created with a USD amount.
* `zeroconfOffer` is the Bitcoin 0-conf offer; see [ZeroConf](/orchestra/zeroconf). `error.code` values are in [Errors](/api/errors).
* `supersededByOperationId` points to the successor after an RBF-replaced deposit; `recoveredFromOperationId` points back. Both are omitted when absent.
* Dedupe on the tuple of `data.id`, `event`, and `data.updatedAt`.
# Orchestra
Source: https://docs.flashnet.xyz/index
Move any asset, instantly.
Flashnet is a modular orchestration stack for any asset. It can move almost anything anywhere, with near-instant, ultra-low-fee settlement. It is not a bridge and it holds no user balances.
# Build with agents
Source: https://docs.flashnet.xyz/orchestra/agents
Skills and documentation access
Agents can read the same Orchestra guides and API contract as developers, with a reviewed skill for choosing and implementing a flow.
## Install the skill
```bash theme={null}
npx skills add https://docs.flashnet.xyz
```
The [Orchestra skill](https://docs.flashnet.xyz/skill.md) covers flow selection, funding, retries, and order tracking. It links to the current guides instead of copying the API reference.
## Connect documentation search
Add this HTTP MCP server to your agent's MCP configuration:
```text theme={null}
https://docs.flashnet.xyz/mcp
```
The server searches and retrieves documentation. It does not authorize Orchestra API requests or move funds. You can also connect through the page menu's **MCP** option.
## Read directly
* [Documentation index](https://docs.flashnet.xyz/llms.txt): find the relevant pages.
* [Quickstart as Markdown](https://docs.flashnet.xyz/orchestra/quickstart.md): read a guide without the site layout. Append `.md` to other page URLs for the same format.
* [Official OpenAPI](https://orchestration.flashnet.xyz/openapi.json): request and response schemas.
* [Full documentation](https://docs.flashnet.xyz/llms-full.txt): a bulk export when the task needs the entire site.
Start with the index and read only the pages the task needs. Fetch [routes and limits](/orchestra/routes-and-limits) for live availability; keep server keys out of prompts and client bundles.
# Dashboard
Source: https://docs.flashnet.xyz/orchestra/dashboard
Keys, affiliates, and payouts
The partner dashboard manages API keys, affiliate profiles, and fee claims; the affiliate dashboard provides read-only earnings reports.
Open the [partner dashboard](https://orchestra.flashnet.xyz/dashboard). Affiliate management and claims require an admin; viewers can inspect affiliate data. Dashboard writes require an approved partner account.
## Manage keys
In **API Keys**, select **Create Key**, choose **Server** for a backend integration or **Client** for scoped application access, then select **Create**.
Keep server keys on your backend; their full value is shown once. Client keys have **Mode**, **Allowed origins**, and **Scopes** controls. Grant only the access needed. Existing keys have **Disable** and **Revoke** actions.
## Register an affiliate
In **Affiliates**, select **New affiliate**:
1. Enter an **Affiliate ID** for API attribution and affiliate sign-in. **Alias** is an optional private dashboard label.
2. Set **Fee rate** in bps, from 1 through 9999. A rate of 100 bps is 1% of the amount after the platform fee.
3. Choose a **Payout destination**, enter its **Payout address**, and select **Create affiliate**.
Pass the ID in `affiliateIds` on quote requests to attribute fees. See [Fees](/orchestra/fees#attach-an-affiliate) for API registration and request overrides.
Select an existing affiliate and open **Settings** to edit its alias, rate, or destination, then select **Save changes**. A changed destination applies to subsequently recorded fees. Existing balances with a recorded destination retain that address. **Disable** stops new accrual and prevents claims for that affiliate.
## Claim fees
Select the affiliate and open **Fees & claims**. **Available now** is unreserved net earnings. **Lifetime earned** includes earnings already paid or reserved; **Pending payout** is reserved for claims, and **Paid out** tracks paid earnings.
Review the balance and its recorded payout destination before submitting. A balance above USD 1 can still be blocked by the payout route's minimum or availability. Read the displayed reason.
* With one eligible group, **Claim available fees** submits the claim immediately.
* With several eligible groups, **Choose fees to claim** opens **Available buckets**. Selecting an entry submits it immediately.
There is no separate confirmation step or partial-amount input. **claim queued.** confirms creation, not receipt. Follow **Recent payouts** for status and transaction details; `completed` confirms payout completion. See [API claims](/orchestra/fees#claim-through-the-api) for programmatic tracking.
## Give an affiliate read-only access
Create a separate reporting key in **API Keys**:
1. Select **Create Key**, then **Client** under **Key type**. Set **Mode** to **browser** for the hosted affiliate dashboard.
2. Set **Allowed origins** to the affiliate dashboard's origin, including the scheme and hostname, without a path.
3. Clear the default **Scopes** and select only `affiliates:read`. Enter the exact affiliate ID in **Affiliate pin (optional)**. The pin is required for this reporting access despite the optional label.
4. Select **Create**. Give the affiliate this client key and its affiliate ID. Never supply a server key.
At the affiliate dashboard sign-in, enter **Affiliate ID** and **Access Key**, then select **View Dashboard**.
The affiliate can read **Dashboard**, **Orders**, and **Payouts** for its own ID. It cannot edit profiles, change destinations, or submit claims. The partner submits claims through its dashboard or server API.
# Deposit addresses
Source: https://docs.flashnet.xyz/orchestra/deposit-addresses
Repeated deposits, fixed destination
A standing deposit address receives repeated deposits and converts them to one configured destination. The destination, slippage, and fee instruction is immutable. Eligible deposits become orders without `/quote` or `/submit`.
## Prerequisites
* An Orchestra account. Create one in the [dashboard](https://orchestra.flashnet.xyz/dashboard). Flashnet reviews new accounts before enabling API access.
* A server key (`fn_...`) from **API keys** in the dashboard. Keep it on your backend. For browsers and apps, use a scoped client key (`fnp_...`); see [Authentication](/api/authentication).
## Create
Select a destination from authenticated `GET /v1/standing-deposit-addresses/destinations`, which returns `{ destinations: [{ chain, asset }] }`. Each pair is eligible through at least one configured source, not every returned address. Availability can change; Hedera is excluded.
Create or fetch the instruction under your customer reference:
```bash curl theme={null}
curl -X PUT https://orchestration.flashnet.xyz/v1/standing-deposit-addresses/customer-4821 \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"destination": { "chain": "spark", "asset": "USDB", "address": "RECIPIENT_ADDRESS" },
"slippageBps": 50,
"feeBps": 25,
"affiliateIds": ["acme-wallet"],
"refundAddresses": { "base": "0xYourBaseRefundAddress", "solana": "YourSolanaRefundAddress" }
}'
```
```typescript TypeScript theme={null}
const ref = encodeURIComponent("customer-4821");
const res = await fetch(`https://orchestration.flashnet.xyz/v1/standing-deposit-addresses/${ref}`, {
method: "PUT",
headers: {
Authorization: "Bearer SERVER_KEY",
"X-Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
destination: { chain: "spark", asset: "USDB", address: "RECIPIENT_ADDRESS" },
slippageBps: 50,
feeBps: 25,
affiliateIds: ["acme-wallet"],
refundAddresses: { base: "0xYourBaseRefundAddress", solana: "YourSolanaRefundAddress" },
}),
});
const standing = await res.json();
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Response shape (`200`):
```text theme={null}
{
standingAddressId,
addresses: { [sourceChain]: address },
enabled
}
```
Returned addresses remain assigned even when a route is unavailable. Check supported funding routes before sending.
* `ref`: 1 to 128 characters, partner-scoped, URL-encoded as one segment. Identical instructions replay; changes return `409 instruction_conflict`.
* `slippageBps`: 0..10000, default 50. `feeBps`: floor of 0..9999, default 0.
* `affiliateIds`: up to 16, frozen at creation; overrides 1..9999, total below 10000.
* `refundAddresses`: source-chain targets for generated-order refunds, not automatic dust refunds.
* Reads require a server key; mutations also require `X-Idempotency-Key`. `GET /{ref}` returns the shape above, without the instruction. Store references, recipient, and fee configuration yourself; there is no list endpoint.
## Deposits
Orchestra verifies deposits and senders, applies confirmation policy, and quotes eligible funds at the live rate within `slippageBps`. Route and partner policy can hold execution.
Funding rules:
* A deposit in the destination asset itself (Base USDC into a Base USDC destination) is held with code `standing_identity_pair`.
* An address for one EVM chain does not authorize the same string on another chain. Fund only the chain the key names.
* Tron standing funding supports USDT only.
* EVM and Solana dust below the route minimum is held and can batch with later deposits of the same asset to the same address into one order. Bitcoin outputs are independent; each must cover the claim fee plus the route minimum.
Eligible Bitcoin deposits can receive a deposit-scoped `zeroconfOffer`. Standing offers require explicit consent, even for allowlisted partners. Without acceptance, processing waits for confirmation. See [ZeroConf](/orchestra/zeroconf#standing-deposits) for the distinct offer shape and decision endpoints.
List deposits newest first:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/standing-deposit-addresses/customer-4821/deposits?limit=50&offset=0" \
-H "Authorization: Bearer SERVER_KEY"
```
Returns `{ deposits, nextOffset }`. Read `status` and `code` for progress or the reason funds are held. `orderId` and `quoteId` link the generated order. The [OpenAPI document](https://orchestration.flashnet.xyz/openapi.json) defines the `StandingDeposit` response schema.
## Pause and refund
Use `PATCH /v1/standing-deposit-addresses/{ref}` with `{ "enabled": false }` to pause or `{ "enabled": true }` to resume. Other fields return `400 immutable_instruction`; there is no delete.
Pause blocks new source commitments, including outstanding batches, while observation continues. Already signed transactions retain their recovery path. Resume retries pause holds, not requested refunds or unrelated review holds. Disabled routes are retryable; restoring policy does not clear every hold. Nothing is refunded automatically.
Request a refund for held deposits:
```bash theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/standing-deposit-addresses/customer-4821/resolve \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "depositIds": ["dep_..."], "refundAddress": "0xYourBaseRefundAddress" }'
```
Send exactly one of `depositIds` (1 to 200, same address and asset; Bitcoin exactly one) or `batchId`, plus a source-chain `refundAddress`. `202 { batchId, status: "refund_requested" }` confirms enqueueing, not broadcast. Ineligible deposits return `409 refund_not_available`.
The worker checks custody and screening; existing orders use order-refund processing. Bitcoin refunds wait for 6 confirmations and deduct network fees. Unconverted Tron deposits require an operator and remain reserved under `standing_tron_refund_requires_operator`.
Accumulation and liquidation addresses are the previous model; see [Reusable addresses (legacy)](/orchestra/legacy/reusable-addresses).
# Fees
Source: https://docs.flashnet.xyz/orchestra/fees
Rates, earnings, and claims
Orchestra charges a platform fee and can collect partner-defined app or affiliate fees on an order.
## Read the quote
The platform rate and minimum depend on the partner and route. App and affiliate rates apply to the amount remaining after the platform fee. Flashnet's share comes out of the gross app or affiliate fee; it is not another deduction from the user's remaining amount. The default share is 20%, subject to configured fee policy.
* `feeAmount`: platform fee; `feeBps` is its rate.
* `appFeeAmount`: gross app or affiliate fee.
* `appFeePlatformCutAmount`: Flashnet's share of that fee.
* `sweepFeeAmount`: sweep fee.
* `networkCostAmount`: destination network cost.
* `roundingFeeAmount`: rounding fee.
Per-recipient entries report `amount`, `platformCutAmount`, and net `recipientAmount`. Use these returned amounts to reconcile earnings. Invoice-billed platform fees are billed separately rather than deducted as `feeAmount`.
On a quote, `totalFeeAmount` sums `feeAmount`, `roundingFeeAmount`, `appFeeAmount`, `sweepFeeAmount`, and `networkCostAmount`. Do not add `appFeePlatformCutAmount` again. Embedded pool or provider costs can affect output without a separate fee field; `totalFeeAmount` is not a measure of every economic cost. [ZeroConf](/orchestra/zeroconf) reports its deposit fee separately in sats.
Quote fee amounts use `feeAsset` and the decimals in `feeAssetDetails`. Accrued earnings use the returned settlement chain and asset. A claim pays to its recorded payout chain, asset, and address. These denominations can differ; interpret raw integer amounts using the relevant asset's decimals.
### Example
For an exact-in amount of 1,000 USD in a six-decimal stablecoin, assume a 5 bps platform fee, a 100 bps affiliate fee, and the default 20% share. This calculation excludes other costs, conversion, and minimum-fee adjustments.
The platform fee is 1,000 x 0.0005. The affiliate fee is 999.50 x 0.01, and Flashnet's share is 9.995 x 0.20.
| Component | USD |
| ------------------- | ------- |
| Platform fee | 0.50 |
| Affiliate base | 999.50 |
| Gross affiliate fee | 9.995 |
| Flashnet share | 1.999 |
| Net earnings | 7.996 |
| Remaining | 989.505 |
The remaining amount is not a guaranteed delivery amount. Keep integer precision through the calculation rather than rounding intermediate values to cents.
## Attach an affiliate
Register the affiliate in the [partner dashboard](https://orchestra.flashnet.xyz/dashboard), or use `PUT /v1/affiliates/:affiliateId` with a server key and `X-Idempotency-Key`. The API requires `feeBps`, `payoutChain`, `payoutAsset`, and `payoutAddress` on every upsert. Rates are 1 through 9999 bps.
Add the registered ID to an authenticated quote request:
```json theme={null}
{ "affiliateIds": ["acme_app"] }
```
Server keys can instead pass `{ "affiliateId": "acme_app", "feeBps": 100 }` as an array entry to override the rate for that request. Client keys cannot override rates; a configured client affiliate pin takes precedence over request IDs.
Inline `appFees` are an alternative on routes that support them: entries contain `recipient` and `fee` in bps. Each rate is 1 through 9999, with at most 16 entries and a combined cap of 10000 bps. Do not combine `appFees` with affiliate IDs. Client keys cannot set inline app fees, and xchain routes reject them; use registered affiliates there.
## Available and claimable
Affiliate earnings accrue net of Flashnet's share. A quoted fee becomes available only after it is recorded and custody is confirmed. Available fees are unpaid and not reserved by another claim.
A claim reserves fees from a selected settlement balance for its recorded payout destination. The server determines the claim amount; partial-amount claims are not supported.
Claimability also depends on the payout path. A normalized USD 1 minimum applies, but the route can require more or be unavailable. Read `claimable`, `unclaimableCode`, and `unclaimableReason` for each balance. Claim creation rechecks eligibility and can require a live payout quote.
Changing an affiliate's payout destination affects subsequently recorded fees. Existing fees with a recorded destination keep it. Review the destination and address for the balance before claiming.
## Claim through the API
Use a partner server key. Affiliate reporting client keys cannot create or inspect claims through these endpoints. For the browser workflow, see [Claim fees](/orchestra/dashboard#claim-fees).
1. Read `GET /v1/affiliate-dashboard/:affiliateId/stats`. Inspect `stats.claimBalances` for the settlement asset, recorded payout destination, available amount, and eligibility.
2. Submit `POST /v1/affiliates/:affiliateId/claim` with `Authorization: Bearer SERVER_KEY` and `X-Idempotency-Key`. Generate a key for each new claim request and reuse it with the same body when retrying that request.
3. Store the returned `claim.id`. Follow `GET /v1/affiliates/:affiliateId/claims/:id`, or list claims at `GET /v1/affiliates/:affiliateId/claims`.
The optional JSON body selects a balance, never a new payout address. Supply `settlementChain` and `settlementAsset` together. To select a recorded destination, also supply all of `payoutChain`, `payoutAsset`, and `payoutAddress`. There is no `amount` field.
For a returned Solana USDC balance with a Solana USDC payout, the body is:
```json theme={null}
{
"settlementChain": "solana",
"settlementAsset": "USDC",
"payoutChain": "solana",
"payoutAsset": "USDC",
"payoutAddress": "RECIPIENT_ADDRESS"
}
```
Replace `RECIPIENT_ADDRESS` with that balance's recorded address. Use the returned chain and asset values for other balances. When several settlement balances exist, the settlement pair is required.
HTTP `201` means the claim was created and payment queued. A `processing` claim has not confirmed delivery; `completed` means its payout completed. For `failed` claims, inspect the failure and current balance before submitting another claim. Funds are not always released immediately. Settlement amounts and delivered payout amounts can differ because of conversion and payout costs.
# Reusable addresses
Source: https://docs.flashnet.xyz/orchestra/legacy/reusable-addresses
Reusable addresses bind one source chain and fee plan to a fixed conversion direction.
New integrations use [standing deposit addresses](/orchestra/deposit-addresses). Retaining a legacy address does not establish current route availability.
## Accumulation addresses
Create an address that converts Solana USDC deposits into USDB on Spark:
```bash theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/accumulation-addresses \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"sourceChain": "solana",
"sourceAsset": "USDC",
"destinationAsset": "USDB",
"recipientSparkAddress": "RECIPIENT_ADDRESS",
"feeBps": 25,
"slippageBps": 50
}'
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Accumulation accepts supported Solana assets or configured deposit chains/assets. `destinationAsset` is `BTC` or `USDB` on Spark. Fees are `feeBps` plus either `appFees` or `affiliateIds`. The response carries `accumulationAddressId`, `depositAddress`, and `subscriptions` (Solana Helius only). The same configuration returns the same address.
Fund the configured asset: native assets use native transfers; EVM tokens use token transfers. For missed deposits, `POST /v1/accumulation-addresses/:idOrAddress/reindex` requests a rescan. `POST /v1/accumulation-addresses/sync` re-registers Solana Helius webhooks. `GET /v1/accumulation-addresses`, `GET /:id`, and `DELETE /:id` list, read, and disable.
## Liquidation addresses
Liquidation accepts Bitcoin L1 deposits for canonical Bitcoin routes, excluding Hedera and xchain-only destinations. Create an address targeting USDC on Base:
```bash theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/liquidation-addresses \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"destinationChain": "base",
"destinationAsset": "USDC",
"destinationAddress": "RECIPIENT_ADDRESS",
"feeBps": 25,
"slippageBps": 50
}'
```
The response carries `liquidationAddressId`, `l1DepositAddress` (fund this), and `sparkAddress` (internal claim address). Each `(txid, vout)` becomes a separate order with `quoteId: null`. Below-floor outputs fail with `amount_too_small`. Eligible deposits may receive per-order [ZeroConf](/orchestra/zeroconf) offers. `GET /v1/liquidation-addresses/orders?id=` or `?label=` lists generated orders.
## Fee plans
The fee plan is resolved and frozen when the address is created; later changes to affiliate profiles do not apply. Inline `appFees` recipients must hold an address on the settlement chain: Solana for most routes, Spark USDB for USDB destinations. BTC-only destinations reject fee plans with `400 unsupported_fee_plan`.
## Differences from standing addresses
* Legacy instructions have one source chain, order tracking, order-scoped ZeroConf, and no address-level refund API. `DELETE` disables an address.
* Standing instructions span source chains and track deposits before orders. They support `/resolve` refunds, reversible `PATCH` pauses, and deposit-scoped ZeroConf.
# Fiat onramp
Source: https://docs.flashnet.xyz/orchestra/onramp
Lightning-funded orders
The Lightning onramp creates an invoice to fund delivery of BTC, USDB, or a stablecoin to a supported destination.
`POST /v1/orchestration/onramp` returns the order, invoice, fees, and expiry. Pay the BOLT11 invoice in `depositAddress` with Cash App or Strike. `paymentLinks.cashApp` opens Cash App with the invoice; the payer app controls its USD display.
## Prerequisites
* An Orchestra account. Create one in the [dashboard](https://orchestra.flashnet.xyz/dashboard). Flashnet reviews new accounts before enabling API access.
* A server key (`fn_...`) from **API keys** in the dashboard. Keep it on your backend. For browsers and apps, use a scoped client key (`fnp_...`); see [Authentication](/api/authentication).
## Flow
`GET /v1/orchestration/estimate` with `sourceChain=lightning&sourceAsset=BTC` returns an indicative `estimatedOut` without creating anything.
Request \$50.00 over Lightning into USDB on Spark:
```bash curl theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/orchestration/onramp \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"destinationChain": "spark",
"destinationAsset": "USDB",
"recipientAddress": "RECIPIENT_ADDRESS",
"amountFiatUsd": "50.00"
}'
```
```typescript TypeScript theme={null}
const res = await fetch("https://orchestration.flashnet.xyz/v1/orchestration/onramp", {
method: "POST",
headers: {
Authorization: "Bearer SERVER_KEY",
"X-Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
destinationChain: "spark",
destinationAsset: "USDB",
recipientAddress: "RECIPIENT_ADDRESS",
amountFiatUsd: "50.00",
}),
});
const onramp = await res.json();
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Example response for exact-in pricing (values vary by quote):
```json theme={null}
{
"orderId": "ord_01J9Q0X3K8M2N7P4R6S8T0V2W4",
"quoteId": "q_01J9Q0X3K8M2N7P4R6S8T0V2W3",
"depositAddress": "lnbc...",
"paymentLinks": {
"cashApp": "https://cash.app/launch/lightning/lnbc...",
"shortUrl": "https://orchestration.flashnet.xyz/pay/8fQ2kL"
},
"amountIn": "44210",
"estimatedOut": "49750000",
"feeAmount": "250000",
"feeAsset": "USDB",
"expiresAt": "2026-09-11T20:59:00.000Z"
}
```
Optional `shortUrl` serves a mobile handoff page or desktop QR page for this order. Use [pay links](/orchestra/pay-links) for repeat checkout.
For Cash App, navigate to `paymentLinks.cashApp` on mobile or encode that URL as a desktop QR code. Never `fetch()` it. Use the invoice for a compatible wallet's Lightning payment flow.
Subscribe to SSE or register a webhook; see [Status](/orchestra/status) and Frontend rules below.
## Amount modes
Send exactly one of `amount` or `amountFiatUsd`.
* `amount`: integer string in sats for `amountMode: "exact_in"` (default), or destination smallest units for `"exact_out"`.
* `amountFiatUsd`: USD string from `"1.00"` to `"50000.00"`, converted to sats at spot. The order and webhooks record `spotUsdPerBtc`.
Fiat requests automatically use exact-out for eligible invoice-billed stablecoin payments; otherwise they use exact-in, where deducted fees reduce delivery. Set `amountMode: "exact_in"` to require that mode. Read the returned `amountMode`, amounts, and fees; partner-invoiced platform fees are billed separately.
* `exact_out` is not supported to `spark:BTC` or `bitcoin:BTC`.
* `slippageBps` is pinned to 1000. Lower values are clamped up; `effectiveSlippageBps` reports what was applied.
* Use returned `expiresAt`. Exact-in normally lasts 24 hours; exact-out and fixed-delivery requests use 5 minutes. Fixed delivery can fall back when unavailable. Reopening does not extend expiry.
* `refundAddress` is a Lightning address (`user@domain`) or an amountless BOLT11 invoice, used if the order fails before the swap. Other values are dropped and named in `ignoredFields`.
## Destinations
Destinations are the `lightning:BTC` entry's `route.to` set on `GET /v2/orchestration/routes`. Common targets:
* USDB on Spark: swap into USDB; fees in USDB.
* BTC on Spark or Bitcoin L1: settle the fee, then deliver BTC; fees in sats.
* USDC on Solana or Base: swap, bridge, and deliver; fees in USDC.
## Restrictions
Not available to residents of New York City. Lightning payer apps impose their own per-payment limits; validate client-side against the `limits.fiatUsd` band from `GET /v1/orchestration/limits`. See [Routes and limits](/orchestra/routes-and-limits).
## Frontend rules
* Keep the API key on your backend. Proxy `/v1/sse/` paths through your server with `Content-Type: text/event-stream`.
* Separate invoice UI expiry from order tracking. Close SSE on `completed`, `failed`, or `refunded`; continue tracking `unfulfilled` for late settlement during the six-hour recovery window from timeout. If SSE fails, poll `GET /v1/orchestration/status?id=ORDER_ID` every 3 seconds.
Live demo: [orchestra.flashnet.xyz/onramp](https://orchestra.flashnet.xyz/onramp). Source: [flashnetxyz/pay-link-example](https://github.com/flashnetxyz/pay-link-example).
# Pay links
Source: https://docs.flashnet.xyz/orchestra/pay-links
Reusable Lightning checkout
A pay link is a durable URL that creates Lightning payment attempts for a fixed destination and recipient.
A fresh visit to `/pay/` starts checkout through a mobile Cash App handoff page or a desktop QR page. The link has no expiry, but each payment attempt has its own invoice deadline. Reopening an attempt does not renew it.
## Create
Pay links are server-key only. Create one that delivers 25 USDC on Base to a fixed recipient:
```bash curl theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/pay-links \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"destinationChain": "base",
"destinationAsset": "USDC",
"recipientAddress": "RECIPIENT_ADDRESS",
"amountOut": "25000000",
"label": "coffee-cart-01"
}'
```
```typescript TypeScript theme={null}
const res = await fetch("https://orchestration.flashnet.xyz/v1/pay-links", {
method: "POST",
headers: {
Authorization: "Bearer SERVER_KEY",
"X-Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
destinationChain: "base",
destinationAsset: "USDC",
recipientAddress: "RECIPIENT_ADDRESS",
amountOut: "25000000",
label: "coffee-cart-01",
}),
});
const { payLink } = await res.json();
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
The response is `201` with `{ payLink }`, including `id`, `shortId`, and a configured `shortUrl`. Supported assets are `USDB`, `USDC`, `USDT`, and `PathUSD`, subject to the [API's destination constraints](/api/pay-links). Hedera is excluded.
## Amount modes
Send exactly one:
* `amountOut`: exact-out delivery in destination smallest units, in whole cents. Invoice-funded fees increase the payer's amount; partner-invoiced platform fees are separate.
* `amountFiatUsd`: `"1.00"` to `"50000.00"`, converted at visit-time spot. Eligible invoice-billed stablecoin payments use exact-out automatically; otherwise exact-in applies and deducted fees reduce delivery.
Use the order's returned economics and `expiresAt`. The payer app controls its USD display.
## Manage
* `GET /v1/pay-links`: paginated list; disabled links are excluded by default.
* `GET /v1/pay-links/:id`: read one link.
* `DELETE /v1/pay-links/:id`: disable with `X-Idempotency-Key`. The URL returns 404; existing orders are unaffected.
## Reconciliation
Orders carry `payLinkId` and `payLinkLabel` in [webhooks](/api/webhook-events). History has no pay-link filter; query by `recipientAddress` and match webhook `payLinkId`.
## Restrictions
Pay links are not available to residents of New York City. Pay-link endpoints are not part of the OpenAPI spec; this page and [Pay links API](/api/pay-links) are the reference.
# Pricing
Source: https://docs.flashnet.xyz/orchestra/pricing
Estimates and price guarantees
An estimate previews a price; a quote supplies funding instructions and pricing valid until `expiresAt`. [Fees](/orchestra/fees) explains the cost breakdown.
## Estimate, quote, fixed delivery
`GET /estimate` is stateless and allocates no deposit address. It needs no key unless you pass affiliate fields. Use it as inputs change.
`POST /quote` requires a key, persists the quote, and returns deposit instructions. Its pricing lasts 2 minutes, subject to slippage.
`deliveryMode: "fixed"` commits to `estimatedOut` with timely, full funding. Finalized underpayment can reduce the target; overpayment does not increase it.
For `/quote` and `/estimate`, fixed delivery requires exact-in, destination membership in `fixedTo`, and partner support. Unsupported requests fail; onramp can fall back for some availability failures, but still rejects unsupported routes. Trust the returned `deliveryMode`.
Both endpoints use route-dependent `slippageBps` defaults and minimums. Omit it to use the route's default.
Fund before `expiresAt`. Eligible late funding uses refreshed market pricing without the original price or fixed-output guarantee. Some expired submissions are rejected.
## Amounts
Amounts are integer strings in smallest units: `"50000000"` is 50 Base USDC; `"100000"` is 0.001 BTC. Read each asset's `decimals` from `/routes`.
* `exact_in` (default): `amount` is source units. Read `amountIn` and `estimatedOut`.
* `exact_out`: `amount` is destination units. Read `targetAmountOut`, `requiredAmountIn`, `maxAcceptedAmountIn`, and `inputBufferBps`. The destination must be in `exactOutTo`. Stablecoin targets must be whole cents, such as multiples of `10000` for a 6-decimal asset.
Onramp and pay links accept `amountFiatUsd` instead of `amount`. Eligible stablecoin onramps can select fiat-capped exact-out when partner fees are invoiced and no app or affiliate fees apply. Explicit `exact_in` prevents that selection. Use the returned mode and amounts.
`/quote` requires `refundAddress` for exact-out and Lightning destinations. Onramp leaves it optional, but missing refund details can obstruct recovery. Supply a valid refund target wherever supported.
Lightning destinations take a BOLT11 invoice as `recipientAddress`: amountless for exact-in; amountless or matching the target for exact-out.
Send the returned `amountIn`. Accepted differences can update input, fees, and output; route minimums, maximums, and funding rules still apply. Dust may create no order, and over-limit funding can be held. Lightning source invoices fix the amount.
# Quickstart
Source: https://docs.flashnet.xyz/orchestra/quickstart
Your first swap
This flow converts 50 USDC on Base into BTC on Spark: discover the route, quote it, fund it, and follow the order.
## Prerequisites
* An Orchestra account. Create one in the [dashboard](https://orchestra.flashnet.xyz/dashboard). Flashnet reviews new accounts before enabling API access.
* A server key (`fn_...`) from **API keys** in the dashboard. Keep it on your backend. For browsers and apps, use a scoped client key (`fnp_...`); see [Authentication](/api/authentication).
Both endpoints are public. `/routes` lists assets and their destinations; `/limits` gives amount bounds. Filter it to the pair:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v2/orchestration/routes"
curl "https://orchestration.flashnet.xyz/v1/orchestration/limits?sourceChain=base&sourceAsset=USDC&destinationChain=spark&destinationAsset=BTC"
```
Read capability from the `route.to` set on the source asset, not from the symbol. Details in [Routes and limits](/orchestra/routes-and-limits).
`/estimate` is public and stateless. Use it to preview the price as inputs change:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/orchestration/estimate?sourceChain=base&sourceAsset=USDC&destinationChain=spark&destinationAsset=BTC&amount=50000000"
```
A quote returns deposit instructions and pricing valid for 2 minutes, subject to slippage. Amounts are integer strings in smallest units: `"50000000"` is 50 Base USDC.
```bash curl theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"sourceChain": "base",
"sourceAsset": "USDC",
"destinationChain": "spark",
"destinationAsset": "BTC",
"amount": "50000000",
"recipientAddress": "RECIPIENT_ADDRESS"
}'
```
```typescript TypeScript theme={null}
const res = await fetch("https://orchestration.flashnet.xyz/v1/orchestration/quote", {
method: "POST",
headers: {
Authorization: "Bearer SERVER_KEY",
"Content-Type": "application/json",
"X-Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
sourceChain: "base",
sourceAsset: "USDC",
destinationChain: "spark",
destinationAsset: "BTC",
amount: "50000000",
recipientAddress: "RECIPIENT_ADDRESS",
}),
});
const quote = await res.json();
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Illustrative response, trimmed; use the values from your own quote:
```json theme={null}
{
"quoteId": "q_...",
"depositAddress": "0x...",
"amountIn": "50000000",
"estimatedOut": "45120",
"expiresAt": "2026-09-10T18:02:00.000Z"
}
```
Send the returned `amountIn` of USDC to `depositAddress` on Base before `expiresAt`. Orchestra detects the deposit and creates the order without a confirm call. Eligible late funding uses refreshed pricing without the original price guarantee; acceptance is not assured.
Poll the quote with a server key until `order` is non-null, then read the order by id:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/orchestration/order?quoteId=QUOTE_ID" \
-H "Authorization: Bearer SERVER_KEY"
curl "https://orchestration.flashnet.xyz/v1/orchestration/status?id=ORDER_ID" \
-H "Authorization: Bearer SERVER_KEY"
```
A successful order reaches `completed`; intermediate stages vary by route. Handle failures and refunds too. [Status](/orchestra/status) covers polling, webhooks, and SSE. If detection misses the deposit, [submit its transaction](/orchestra/quotes#when-to-call-submit); order creation may require deposit verification.
## Next
* [Quotes](/orchestra/quotes) for deposit rules and the `/submit` body.
* [Pricing](/orchestra/pricing) for estimate versus quote and amount modes; [Fees](/orchestra/fees) for who pays what.
* [Fiat onramp](/orchestra/onramp) for USD in over Lightning through one call.
# Quotes
Source: https://docs.flashnet.xyz/orchestra/quotes
Deposit-driven swaps
A quote supplies deposit instructions and pricing valid for 2 minutes, subject to slippage. Choose a destination in the source asset's `route.to` set. Orchestra detects funding, creates the order, and delivers to `recipientAddress` without a separate confirm call.
## Prerequisites
* An Orchestra account. Create one in the [dashboard](https://orchestra.flashnet.xyz/dashboard). Flashnet reviews new accounts before enabling API access.
* A server key (`fn_...`) from **API keys** in the dashboard. Keep it on your backend. For browsers and apps, use a scoped client key (`fnp_...`); see [Authentication](/api/authentication).
## Flow
Call `GET /v1/orchestration/estimate` with the four route fields and `amount` on every input change. It is public and stateless.
Lock the price with `POST /v1/orchestration/quote`. Stablecoin on Base into BTC on Spark:
```bash curl theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"sourceChain": "base",
"sourceAsset": "USDC",
"destinationChain": "spark",
"destinationAsset": "BTC",
"amount": "50000000",
"recipientAddress": "RECIPIENT_ADDRESS"
}'
```
```typescript TypeScript theme={null}
const res = await fetch("https://orchestration.flashnet.xyz/v1/orchestration/quote", {
method: "POST",
headers: {
Authorization: "Bearer SERVER_KEY",
"Content-Type": "application/json",
"X-Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
sourceChain: "base",
sourceAsset: "USDC",
destinationChain: "spark",
destinationAsset: "BTC",
amount: "50000000",
recipientAddress: "RECIPIENT_ADDRESS",
}),
});
const quote = await res.json();
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Another pair uses the same request shape: 0.001 BTC on Spark into USDC on Solana.
```bash theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"sourceChain": "spark",
"sourceAsset": "BTC",
"destinationChain": "solana",
"destinationAsset": "USDC",
"amount": "100000",
"recipientAddress": "RECIPIENT_ADDRESS"
}'
```
Send `amountIn` of the source asset to `depositAddress` before `expiresAt`. Include `depositMemo` when returned. Eligible late funding uses refreshed pricing without the original guarantee; some expired submissions are rejected. See [Pricing](/orchestra/pricing).
Poll `GET /v1/orchestration/order?quoteId=QUOTE_ID` with a server key until `order` is non-null, then follow the order on [Status](/orchestra/status) by webhook, SSE, or `GET /v1/orchestration/status?id=ORDER_ID`.
## Deposit address by source chain
Send the quoted token or native asset on the source chain. If detection misses, submit the transaction:
```json theme={null}
{ "quoteId": "QUOTE_ID", "txHash": "0x...", "sourceAddress": "0x..." }
```
`sourceAddress` is optional except on shared deposit addresses, where it identifies the sender.
`depositAddress` is a Solana address. Deposits are detected through Helius webhooks. Fallback submit body:
```json theme={null}
{ "quoteId": "QUOTE_ID", "txHash": "", "sourceAddress": "" }
```
`depositAddress` is a Spark address. Deposits are detected by the Spark ingress scan. Fallback submit body:
```json theme={null}
{ "quoteId": "QUOTE_ID", "sparkTxHash": "", "sourceSparkAddress": "" }
```
Spark USDB uses a quote-scoped sell address. Use the address returned for each quote. `sourceSparkAddress` is required for shared deposit addresses and optional otherwise.
`depositAddress` is an L1 address. Deposits are detected over ZMQ from a Bitcoin node. Fallback submit body:
```json theme={null}
{ "quoteId": "QUOTE_ID", "bitcoinTxid": "<64 hex>", "bitcoinVout": 0 }
```
Set `bitcoinVout` to the output paying `depositAddress`; `0` is only an example. Include it because some routes require it. L1 deposits may receive a [ZeroConf offer](/orchestra/zeroconf).
`depositAddress` is a BOLT11 invoice and the quote also returns `lightningReceiveRequestId`. Paying the invoice creates the order. Fallback submit body:
```json theme={null}
{ "quoteId": "QUOTE_ID", "lightningReceiveRequestId": "" }
```
Orchestra populates the id from the quote. Lightning funding is detected automatically; for cross-chain quotes, `/submit` can return an existing order but cannot create one before detection. Keep polling if it returns `409 xchain_lightning_submissionless`.
For TON, Tron, XRP, Litecoin, Zcash, and Hedera, use the returned address and memo or tag exactly. Transfer the quoted asset: native coins use native transfers, tokens and TON jettons use their token transfer operation. Submit the source transaction id as `txHash`, with `sourceAddress` when required for a shared address.
## When to call /submit
`POST /v1/orchestration/submit` reports funding when detection misses it or you have the transaction id already. Authenticate with the quote's partner key and send the body for its source chain. Repeating the same deposit returns the existing order.
An order id is not guaranteed at broadcast. Bitcoin and TON paths can require observed deposit proof. After a definitive verification rejection, wait for evidence and use a new idempotency key for the next recovery attempt. Reuse the same key after a timeout or 5xx; see [Idempotency](/api/overview#idempotency). Keep polling and do not send another payment. On success, use the returned `orderId` and `status`, plus `readToken` for client keys.
## Quote response
* `quoteId`: `q_...`; use it for `/order` and `/submit`.
* `depositAddress`: where the user sends the source asset. Use this quote's instructions.
* `depositMemo`: memo or tag the deposit must carry; present only on provider routes that require one.
* `amountIn`: source amount the price is based on.
* `estimatedOut`: destination output; fixed delivery commits to it with timely, full funding. See [funding conditions](/orchestra/pricing).
* `feeAmount`, `feeBps`: platform fee in `feeAsset` units and its rate.
* `totalFeeAmount`: sum of `feeAmount`, `roundingFeeAmount`, `appFeeAmount`, `sweepFeeAmount`, and `networkCostAmount`.
* `feeAsset`: denomination of every fee field; see [Fees](/orchestra/fees).
* `route`: path labels, which may be symbols or asset ids. Do not use them as catalog keys.
* `expiresAt`: ISO timestamp, 2 minutes after creation.
* `priceLockMode`, `lockedMinAmountOut`: optional price-lock policy and output floor. A price lock is separate from fixed delivery.
* `readToken`: client keys only; bound to this quote and passed on status reads as `readToken` or `X-Read-Token`.
* Exact-out quotes add `amountMode`, `targetAmountOut`, `requiredAmountIn`, `maxAcceptedAmountIn`, and `inputBufferBps`.
* Lightning sources add `lightningReceiveRequestId`.
## Rules
* Use each quote's returned deposit instructions; do not reuse cached instructions.
* A quote is bound to the partner that created it. Only a key from the same partner can submit it.
* `affiliateId` and `affiliateIds` require `Authorization`.
* Send `refundAddress` whenever you can. It is required for exact-out, for Lightning destinations, and for deposits from BNB, native TON, Tron, XRP, Litecoin, and Zcash. Without it, Orchestra refunds to the detected source address only when that is safe, and never to an exchange's shared sender address.
# Routes and limits
Source: https://docs.flashnet.xyz/orchestra/routes-and-limits
Assets and route limits
Routes list which assets can reach which destinations; limits give amount bounds for a pair. Read `/routes` for capability and decimals, `/limits` for form validation, then `/estimate` for pricing and `/quote` when the user commits. Do not hardcode availability or bounds.
## Routes
`GET /v2/orchestration/routes` returns `{ assets: [...] }`, one entry per asset. The `id` is `:`, for example `base:USDC`, `ton:GRAM`, `hypercore:USDC`. Fetch it:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v2/orchestration/routes"
```
Illustrative asset entry; read current capabilities from the response.
```json theme={null}
{
"id": "base:USDC",
"chain": "base",
"asset": "USDC",
"assetDisplayName": "USD Coin",
"assetDisplaySymbol": "USDC",
"chainDisplayName": "Base",
"chainIcon": "/chain-base.svg",
"contractAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"decimals": 6,
"chainId": "8453",
"route": {
"to": { "except": ["litecoin:LTC"] },
"exactOutTo": ["lightning:BTC"],
"fixedTo": ["bitcoin:BTC", "lightning:BTC", "spark:BTC", "spark:USDB"]
}
}
```
Read the source asset's sets for the requested destination:
* `to`: supported destinations.
* `exactOutTo`: destinations supporting exact-out amounts.
* `fixedTo`: destinations supporting fixed delivery.
Each set is `"all"` (every other asset), a list of ids (empty means none), or an object with an `except` list. Availability also depends on partner configuration and live pricing. Discover routes with the same partner key used for quotes when applicable.
`contractAddress` identifies the token; native assets use null. `chainId` identifies the network where supplied. `decimals` is the smallest-unit exponent for that specific asset: Base USDC uses 6, BSC USDC 18, and Hypercore USDC 8. Never infer decimals from the ticker.
Read capability from set membership, not from the symbol. Two assets that share a ticker on different chains are different ids with different sets, and an id that is missing from the response is never routable.
## Limits
`GET /v1/orchestration/limits` returns `{ generatedAt, routes: [...] }` with bounds per canonical route. Filter by `sourceChain`, `sourceAsset`, `destinationChain`, and `destinationAsset`:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/orchestration/limits?sourceChain=lightning&sourceAsset=BTC&destinationChain=spark&destinationAsset=USDB"
```
Illustrative route entry, trimmed; use live bounds:
```json theme={null}
{
"sourceChain": "lightning",
"sourceAsset": "BTC",
"destinationChain": "spark",
"destinationAsset": "USDB",
"direction": "sell",
"exactOutEligible": true,
"fixedEligible": false,
"limits": {
"orderNotionalUsd": {
"minCents": "100",
"maxCents": "11220000",
"source": "runtime_order_bounds"
},
"exactIn": {
"supported": true,
"requestAmount": {
"leg": "source",
"chain": "lightning",
"asset": "BTC",
"minAmountSmallest": "1200",
"maxAmountSmallest": "98000000",
"minUsdCents": "100",
"maxUsdCents": "11220000"
},
"constraints": ["runtime_order_bounds"]
},
"exactOut": { "supported": true, "requestAmount": {}, "constraints": [] },
"fiatUsd": {
"supported": true,
"min": "1.00",
"max": "50000.00",
"surfaces": ["onramp", "pay_link"]
},
"dynamicProviderLimits": {
"possible": false,
"components": [],
"description": null
},
"constraints": [
{
"id": "runtime_order_bounds",
"amountMode": "exact_in",
"leg": "source",
"source": "runtime_order_bounds",
"description": "Operator-tuned notional bounds"
}
]
}
}
```
Direction is buy, sell, or xchain. The notional band is operator-tuned and changes without notice. The exact-in and exact-out blocks give the request-amount bounds in smallest units and in cents. The fiat band is present only when the source is Lightning BTC. When `dynamicProviderLimits.possible` is true, live pricing can still reject an amount inside the published band. Every bound has a typed entry in `constraints` whose `source` is one of runtime\_order\_bounds, flashnet\_static\_limit, bitcoin\_l1\_delivery, provider\_quote, or fiat\_amount.
Three minimums are static and enforced in code: 5,000 sats of swap input from Bitcoin L1 (published as `minAmountSmallest: "5149"` once the static deposit fee is added), 1,200 sats from Spark, and 10,000 sats for delivery to Bitcoin L1.
Some cross-chain planner routes appear on `/routes` but not on `/limits`. Treat `/limits` as a guardrail for your form, not as an execution guarantee: `/estimate` and `/quote` can still return `amount_too_small`, `amount_too_large`, `amount_exceeds_liquidity`, or `route_unavailable` for an amount inside the published band.
Both endpoints are rate limited to 60 requests per minute per IP. Responses carry `X-RateLimit-*` headers and exhaustion returns `429 rate_limited`. See [Rate limits](/api/rate-limits).
# Attestation
Source: https://docs.flashnet.xyz/orchestra/security/attestation
Verify worker identity
`GET /v1/attestation` requests evidence of the worker's enclave identity, bound to a caller-supplied nonce.
Use a fresh nonce of 16 to 128 printable, non-space ASCII characters:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/attestation?nonce=$(openssl rand -hex 16)"
```
The response can include `enclave`, `nonce`, and a base64 COSE\_Sign1 `document`, plus verification results:
* `verified`: server-side verification result. Failures include `reason`.
* `pcrsPinned`: whether verification used configured image measurements. `verified: true` alone does not establish build matching.
* `moduleId`, `pcr0`, `timestamp`: returned after successful verification.
Invalid nonces return HTTP 400 with `error: "invalid_nonce"`. Worker failures return HTTP 503 with `enclave: false` and an error string. A missing document yields `verified: false` and `reason: "no_document"`. Do not infer deployment type from `enclave: false`.
To verify:
Decode the document, verify its signature and certificate chain to a trusted AWS Nitro root, check your nonce and timestamp freshness, and compare measurements against a trusted release. Obtain release-bound measurements from Flashnet before claiming build verification; the response's `pcr0` is evidence to compare, not its own trust anchor.
# Risk & compliance
Source: https://docs.flashnet.xyz/orchestra/security/risk-and-compliance
Screening and order review
Flashnet screens orders for sanctions and illicit-finance risk before they settle. The checks run automatically through [Elliptic](https://www.elliptic.co/) and other third-party providers used across the industry for anti-money-laundering and sanctions compliance.
## What we screen
Every order carries a source address and a destination address. Flashnet checks both against third-party risk data from Elliptic and other providers, which covers Bitcoin and the other chains Orchestra routes across. The check looks for sanctioned entities, known illicit actors, and exposure to high-risk activity such as theft, fraud, and darknet markets.
Screening is part of order processing. It needs no extra step from your integration and does not change the shape of the API response.
## What happens on a result
An order that clears screening settles normally. An order that comes back high-risk is flagged for compliance review before it can settle, and an order tied to a sanctioned or prohibited address is rejected. A flagged order surfaces through the normal order status, so your integration reacts the same way it does to any other state change.
Flashnet settles without taking custody of user funds. Screening applies to the addresses on each order rather than to balances we hold, because we hold none.
## Law enforcement and regulatory requests
Law enforcement and regulatory authorities can submit formal requests to Flashnet's legal team at [legal@flashnet.xyz](mailto:legal@flashnet.xyz).
## Our commitment
Screening is built into how Orchestra processes orders, not a manual pass added after settlement. Flashnet runs these checks continuously, keeps a durable record of every screening decision, and aligns its controls with global standards for anti-money-laundering and sanctions enforcement.
# Order status
Source: https://docs.flashnet.xyz/orchestra/status
States and delivery updates
Order status tracks execution from deposit to delivery or recovery. Poll for the order snapshot, receive webhooks, or stream current state over SSE.
## States
In flight:
* `processing`: created or awaiting deposit.
* `confirming`: awaiting source confirmations.
* `awaiting_approval`: pending offer or review.
* `swapping`: executing a swap.
* `bridging`: crossing chains.
* `delivering`: sending to the recipient.
* `refunding`: refund in flight.
### Outcomes
* `completed`: delivered.
* `refunded`: funds returned.
* `failed`: execution or refund failure.
* `expired`: expired without deposit.
* `unfulfilled`: unconfirmed or replaced deposit; can resume.
`awaiting_approval` can mean a pending [ZeroConf offer](/orchestra/zeroconf) or an operator hold. Offer accept/decline applies only to a pending `zeroconfOffer`, not to the state alone. Review outcomes can carry `reviewStatus`; internal `paused` states appear as `processing`.
`unfulfilled` can resume after a late deposit. Keep listening and check `supersededByOperationId`. For failures, read `order.errorCode` when polling or `data.error.code` in webhooks.
A Bitcoin deposit replaced by RBF leaves the original order `unfulfilled` with `supersededByOperationId` pointing at the successor. The successor carries `recoveredFromOperationId`. Each time that link advances, `order.superseded` fires on the original.
## Refunds
Execution outside the quote's bounds can trigger a refund. Supply `refundAddress` on the quote. Without a valid refund target, or while an operator hold applies, recovery may need manual review. See [Errors](/api/errors).
## Stages
The status response includes `stages[]`: execution milestones and their `completedAt` timestamps. Use them for progress detail, not as a replacement for order status.
## Reading status
Use webhooks in production. Use SSE for user-facing progress. Poll as the fallback.
A quote becomes an order when the deposit lands, so the first thing to poll is the quote. `GET /v1/orchestration/order` needs a server key and returns `{ quote, order, stages }` with `order` null until the deposit is detected:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/orchestration/order?quoteId=QUOTE_ID" \
-H "Authorization: Bearer SERVER_KEY"
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Status requires authentication and returns `{ order, stages }`; read `order.status` for the current state. Server keys use exactly one of `id`, `quoteId`, or `txHash`; multiple transaction-hash matches return `400 ambiguous_query`. Before an order exists, status returns `404 not_found`:
```bash theme={null}
curl "https://orchestration.flashnet.xyz/v1/orchestration/status?id=ORDER_ID" \
-H "Authorization: Bearer SERVER_KEY"
```
Client keys need `orders:read` and a matching read token: use `quoteId` with the quote token, or `id` with a submit/onramp token. Send `X-Read-Token` or `?readToken=`. Client keys cannot look up by `txHash`.
Server keys can list recipient orders through `/v1/orchestration/history?address=RECIPIENT_ADDRESS`, with `status`, `limit` (default 50, max 200), and `offset`. Poll every 3 seconds; see [Rate limits](/api/rate-limits).
Register an endpoint with a server key. The `secret` is returned once:
```bash theme={null}
curl -X POST "https://orchestration.flashnet.xyz/v1/webhooks" \
-H "Authorization: Bearer SERVER_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{ "url": "https://example.com/flashnet/webhook" }'
```
Each body is `{ event, timestamp, data }`, with an order snapshot in `data`. Not every transition emits an event. [Webhook events](/api/webhook-events) lists the catalog and the separate recovery subscription.
`X-Flashnet-Timestamp` is milliseconds since epoch. `X-Flashnet-Signature` is the hex HMAC-SHA256 of `timestamp + "." + rawBody`. Verify raw bytes before parsing JSON. This example uses a receiver-chosen five-minute clock tolerance:
```javascript theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyFlashnetWebhook(rawBody, headers, secret, now = Date.now()) {
const timestamp = headers["x-flashnet-timestamp"];
const signature = headers["x-flashnet-signature"];
if (typeof timestamp !== "string" || !/^\d+$/.test(timestamp)) return false;
if (typeof signature !== "string" || !/^[0-9a-f]{64}$/i.test(signature)) return false;
const sentAt = Number(timestamp);
if (!Number.isSafeInteger(sentAt) || Math.abs(now - sentAt) > 300_000) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
```
Delivery is retried until a 2xx response, after 10s, 30s, 2m, 10m, 30m, 2h, 6h, and 24h, then marked failed. Retries have fresh header timestamps and signatures; the JSON body stays unchanged. Events can duplicate or arrive out of order: durably dedupe on `(data.id, event, data.updatedAt)`, not `timestamp`.
Open a stream per order through an authorized backend proxy. Keep server keys on the backend:
```javascript theme={null}
const stream = new EventSource(`/api/orders/${orderId}/events`);
stream.addEventListener("status", (e) => {
const { status } = JSON.parse(e.data);
if (["completed", "failed", "refunded"].includes(status)) stream.close();
});
```
Connections start with current state, followed by live updates and 15-second heartbeats. Reconnects do not replay missed transitions. Direct client-key streams require `orders:sse` and an order-bound token; quote tokens cannot authorize them. See [SSE](/api/sse) for authentication, frame formats, and close behavior.
# ZeroConf
Source: https://docs.flashnet.xyz/orchestra/zeroconf
Bitcoin credit before confirmation
ZeroConf offers credit for eligible Bitcoin L1 deposits before confirmation, in exchange for a fee. Use the returned `expiresAt` for the decision deadline; offers currently last five minutes from creation, not receipt.
## Mechanism
Eligibility depends on partner and route policy and an available Spark plan. An offer is not guaranteed. Under the default ordinary-order policy, `zeroconfOffer.status: "pending"` moves the order to `awaiting_approval`. Acceptance starts the credit claim and returns `processing`; the delivery pipeline still has to run. A configured legacy policy can auto-accept ordinary offers. Standing deposits always require explicit consent.
Declining or expiry falls back to 1 confirmation. Accepted credit is single-leg: confirmation releases no extra amount.
## Scope
* Source chain `bitcoin`, `exact_in` only. Exact-out orders are always confirmation-based.
* Read the order or standing deposit for an offer; quote responses do not predict one.
* The credited amount must meet the 1,000-sat execution floor.
## Ordinary orders
Act only when `status` is `awaiting_approval` and `zeroconfOffer.status` is `pending`. Accept the offer:
```bash curl theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/orchestration/zeroconf/accept \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "orderId": "ORDER_ID" }'
```
```typescript TypeScript theme={null}
const res = await fetch("https://orchestration.flashnet.xyz/v1/orchestration/zeroconf/accept", {
method: "POST",
headers: {
Authorization: "Bearer SERVER_KEY",
"X-Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({ orderId: "ORDER_ID" }),
});
const { status } = await res.json(); // "processing"
```
Replace `SERVER_KEY` with your `fn_...` key. Use a new `X-Idempotency-Key` for each operation and reuse it when retrying that same request. Amounts are integer strings in the asset's smallest unit: `"100000"` is 0.001 BTC; `"50000000"` is 50 USDC on Base. Read each asset's `decimals` from `/routes`.
Decline it instead:
```bash theme={null}
curl -X POST https://orchestration.flashnet.xyz/v1/orchestration/zeroconf/decline \
-H "Authorization: Bearer SERVER_KEY" \
-H "X-Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "orderId": "ORDER_ID" }'
```
* `409 expired`: deadline passed. `409 conflict`: order state changed; refresh.
* `400 invalid_state`: order or offer is not awaiting a decision.
### Offer fields
* `version`: schema version, `1`.
* `status`: `pending`, `accepted`, `declined`, `expired`, or `confirmed` (the transaction reached 1 confirmation before a decision).
* `quoteId`, `sparkAddress`: Spark quote and credit destination.
* `txid`, `vout`: the L1 output being credited.
* `depositSats`: gross output value.
* `instantSats`: amount credited on accept.
* `feeSats`: Spark's static-deposit fee, not a Flashnet fee.
* `expiresAt`: authoritative decision deadline.
* `offeredAt`, `resolvedAt`: offer and decision timestamps.
* `sparkTxid`: Spark transaction once the claim runs.
`depositSats = instantSats + feeSats`. Read `feeSats` from each offer; it changes per deposit. An order whose `zeroconfOffer` is `{ status: "denied", deniedAt }` received no offer and has nothing to act on.
## Standing deposits
Standing offers require partner allowlisting and explicit deposit-scoped acceptance, even when legacy auto-accept is enabled. The public shape is separate from the ordinary order offer:
```text theme={null}
{ id, status, expiresAt, depositSats, feeSats, creditSats }
status: pending | accepted | declined | expired | superseded
```
Amounts are integer strings in sats: `depositSats = creditSats + feeSats`. Provider-plan fields are not returned. Confirmation can supersede an offer.
Send `POST /v1/standing-deposit-addresses/{ref}/deposits/{depositId}/zeroconf/accept` or `/decline` with `{ "offerId": "OFFER_ID" }`, a server key, and `X-Idempotency-Key`. Accept returns `202 { deposit }`; decline returns `200 { deposit }`. Use the offer's `id`; no order need exist yet.
On `409 standing_zeroconf_offer_conflict`, refresh the deposit. Retry an uncertain decision with the same idempotency key. Without acceptance, processing waits for confirmation.
# Rewards API
Source: https://docs.flashnet.xyz/rewards/integrators/api
REST endpoints for rewards, endorsements, Flashpoints, and stats
REST API for querying user rewards, configuring reward endorsements, reading Flashpoints data, and checking system stats.
## Base URL
```
https://rewards.flashnet.xyz
```
All endpoints are prefixed with `/v1`.
Swagger docs are available at `/docs`.
***
## Rewards
### Get Rewards Leaderboard
Returns list of users sorted by USDB balance with their rewards info.
```bash theme={null}
GET /v1/rewards/?limit=100&offset=0
```
**Parameters:**
* `limit` - Max results (default 100, max 500)
* `offset` - Pagination offset
**Response:**
```json theme={null}
{
"rewards": [
{
"pubkey": "02abc...def",
"usdbBalance": {
"raw": "10000000000",
"display": "10000"
},
"volumeUtcToday": {
"swapSats": 5000000,
"swapCount": 12
},
"rewardsBracket": 3,
"rewardsPercent": 5.0,
"estimatedSatsToday": 1370
}
],
"meta": {
"total": 830,
"limit": 100,
"offset": 0
}
}
```
### Get User Summary
Returns current USDB balance, today's volume, rewards bracket, and projected rewards.
```bash theme={null}
GET /v1/rewards/:pubkey
```
**Response:**
```json theme={null}
{
"pubkey": "02abc...def",
"usdbBalance": {
"raw": "10000000000",
"display": "10000"
},
"volumeUtcToday": {
"swapSats": 5000000,
"swapCount": 12
},
"rewardsBracket": 3,
"rewardsPercent": 5.0,
"estimatedSatsToday": 1370
}
```
Excluded addresses (LP pools, burn addresses) return a `400` status with `{ "reason": "This address is excluded and does not earn rewards" }`.
**Rewards Brackets:**
| Bracket | `volumeUtcToday.swapSats` Threshold | Rewards |
| ------- | ----------------------------------- | ------- |
| 0 | `>= 0` | 3.5% |
| 1 | `>= 500000` | 4.0% |
| 2 | `>= 2500000` | 4.5% |
| 3 | `>= 5000000` | 5.0% |
| 4 | `>= 10000000` | 5.5% |
| 5 | `>= 25000000` | 6.0% |
### Get Payout History
Returns paginated list of rewards payouts for a user.
```bash theme={null}
GET /v1/rewards/:pubkey/payouts?limit=30&offset=0
```
**Parameters:**
* `limit` - Max results (default 30)
* `offset` - Pagination offset
**Response:**
```json theme={null}
{
"pubkey": "02abc...def",
"payouts": [
{
"day": "2026-02-09",
"usdbBalance": {
"raw": 10000000000,
"display": 10000
},
"volume": {
"swapSats": 5000000
},
"rewardsBracket": 3,
"annualRewardsBps": 500,
"rewardsPayoutSats": 1020,
"endorsementsPayoutSats": 350,
"payoutSats": 1370,
"status": "paid",
"txId": "abc123...",
"createdAt": "2026-02-09T00:05:00Z",
"paidAt": "2026-02-10T00:05:00Z"
}
],
"pagination": { "total": 45, "limit": 30, "offset": 0 }
}
```
`payoutSats` is the sum of `rewardsPayoutSats` and `endorsementsPayoutSats`.
***
## Endorsements
Endorsements let a pubkey direct part of its future daily rewards payout to one or more recipient pubkeys.
### Canonical YAML Templates (for Signing)
Create and update both use `action: upsert`. The API creates a new record or updates the existing `(endorser, to)` pair.
**Create endorsement template:**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: upsert
endorser: ""
to: ""
ratioBps: <1_to_10000>
timestampNonce: ""
```
**Update endorsement template:**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: upsert
endorser: ""
to: ""
ratioBps:
timestampNonce: ""
```
**Delete endorsement template:**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: delete
endorser: ""
to: ""
timestampNonce: ""
```
The client signs the SHA-256 hash of the YAML payload with the endorser private key and sends that signature as hex.
### Get Endorsements
```bash theme={null}
GET /v1/rewards/:pubkey/endorsements
```
**Response:**
```json theme={null}
{
"endorser": "02abc...def",
"endorsements": [
{
"to": "03fed...cba",
"ratioBps": 3000
},
{
"to": "02012...999",
"ratioBps": 2000
}
]
}
```
### Create or Update Endorsement
Creates or updates one endorsement rule for `to` under the endorser.
```bash theme={null}
POST /v1/rewards/:pubkey/endorsements
```
**Request body:**
```json theme={null}
{
"endorser": "02abc...def",
"to": "03fed...cba",
"ratioBps": 3000,
"timestampNonce": "01958d67-2d1a-7a8a-bf8a-5b0a947f8f11",
"signature": "30440220..."
}
```
**Signing payload (canonical YAML):**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: upsert
endorser: "02abc...def"
to: "03fed...cba"
ratioBps: 3000
timestampNonce: "01958d67-2d1a-7a8a-bf8a-5b0a947f8f11"
```
The API verifies the signature against the endorser pubkey using secp256k1 over the SHA-256 hash of this YAML payload.
**Response:**
```json theme={null}
{
"endorser": "02abc...def",
"endorsements": [
{
"to": "03fed...cba",
"ratioBps": 3000
}
]
}
```
### Delete Endorsement
Deletes one endorsement mapping for the endorser.
```bash theme={null}
DELETE /v1/rewards/:pubkey/endorsements/:toPubkey
```
**Request body:**
```json theme={null}
{
"timestampNonce": "01958d67-41c1-7dd4-8aa3-ef3575a84ab5",
"signature": "30450221..."
}
```
**Signing payload (canonical YAML):**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: delete
endorser: "02abc...def"
to: "03fed...cba"
timestampNonce: "01958d67-41c1-7dd4-8aa3-ef3575a84ab5"
```
**Response:**
```json theme={null}
{
"ok": true
}
```
### Endorsement Rules and Validation
* `pubkey`, `endorser`, and `to` must be 66-char compressed pubkey hex strings.
* `endorser` in body must match `:pubkey` in the route.
* `ratioBps` must be between `1` and `10000`.
* Sum of all active endorsement ratios for an endorser cannot exceed `10000`.
* Endorser cannot endorse to the same pubkey.
* `timestampNonce` must be UUIDv7 and within a 5-minute window.
* Each nonce can only be used once (replay protected).
Common `400` errors:
* `{ "error": "Body endorser must match route pubkey" }`
* `{ "error": "Cannot endorse to the same pubkey. Use rewards payee reassignment instead." }`
* `{ "error": "Total endorsement ratio cannot exceed 10000 bps" }`
* `{ "error": "Nonce has already been used" }`
* `{ "error": "Signature verification failed" }`
***
## Flashpoints
### Get Points Leaderboard
Returns list of users sorted by current Flashpoints with their point balances and volume.
```bash theme={null}
GET /v1/points/?limit=100&offset=0
```
**Parameters:**
* `limit` - Max results (default 100, max 500)
* `offset` - Pagination offset
**Response:**
```json theme={null}
{
"points": [
{
"pubkey": "02abc...def",
"currentPoints": 1250000,
"lifetimePoints": 1500000,
"rank": 1,
"projectedDailyPoints": 1236,
"volumeUtcToday": {
"swapSats": 5000000,
"liquiditySats": 0
}
}
],
"meta": {
"total": 830,
"limit": 100,
"offset": 0
}
}
```
### Get User Points
Returns Flashpoints balance, rank, and projected daily earnings.
```bash theme={null}
GET /v1/points/:pubkey
```
**Response:**
```json theme={null}
{
"pubkey": "02abc...def",
"currentPoints": 1250000,
"lifetimePoints": 1500000,
"rank": 42,
"projectedDailyPoints": 1236,
"volumeUtcToday": {
"swapSats": 5000000,
"liquiditySats": 0
}
}
```
### Get Points History
Returns paginated list of point earning events.
```bash theme={null}
GET /v1/points/:pubkey/history?limit=30&offset=0
```
**Parameters:**
* `limit` - Max results (default 30)
* `offset` - Pagination offset
**Response:**
```json theme={null}
{
"pubkey": "02abc...def",
"events": [
{
"type": "daily_award",
"points": 1236,
"day": "2026-02-09",
"metadata": {},
"createdAt": "2026-02-10T00:05:00Z"
}
],
"pagination": { "total": 45, "limit": 30, "offset": 0 }
}
```
***
## Stats
### Get Global Stats
Returns system-wide statistics.
```bash theme={null}
GET /v1/stats
```
**Response:**
```json theme={null}
{
"usdb": {
"totalHolders": 830,
"totalBalanceRaw": 2500000000000,
"totalBalanceUsdb": 2500000.00
},
"volume24h": {
"activeTraders": 145,
"totalVolumeSats": 850000000,
"totalSwaps": 2340
},
"payouts": {
"pendingCount": 12,
"pendingSats": 140000,
"totalPaidSats": 5000000
},
"processor": {
"lastPointsDay": "2026-02-09",
"lastPayoutDay": "2026-02-09"
},
"updatedAt": "2026-02-10T12:34:56Z"
}
```
### Health Check
```bash theme={null}
GET /v1/stats/health
```
**Response:**
```json theme={null}
{
"status": "healthy",
"lastPayoutDay": "2026-02-09",
"lastPointsDay": "2026-02-09"
}
```
Returns `"status": "degraded"` if either payout or points processing is behind.
***
## Error Handling
Validation errors usually return:
```json theme={null}
{
"error": "Invalid pubkey format. Expected 66 character hex string."
}
```
Excluded pubkeys on `GET /v1/rewards/:pubkey` return:
```json theme={null}
{
"reason": "This address is excluded and does not earn rewards"
}
```
Server-side errors may return:
```json theme={null}
{
"error": "INTERNAL_SERVER_ERROR",
"message": "..."
}
```
Common status codes:
* `400` - Bad request (validation errors, excluded addresses)
* `404` - Not found
* `500` - Internal server error
# Rewards Delegation
Source: https://docs.flashnet.xyz/usdb/delegation
Use endorsements to direct a share of USDB daily rewards to other pubkeys
Rewards delegation is implemented as **endorsements** in the Rewards API.
An endorser signs a request that assigns part of future daily reward payouts to one or more recipient pubkeys.
## High-Level YAML Example
**Create delegation (upsert):**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: upsert
endorser: "02abc123...compressed_pubkey_hex"
to: "03def456...compressed_pubkey_hex"
ratioBps: 2500
timestampNonce: "01958d67-2d1a-7a8a-bf8a-5b0a947f8f11"
```
**Revoke delegation (delete):**
```yaml theme={null}
kind: rewards_endorsement
version: 1
action: delete
endorser: "02abc123...compressed_pubkey_hex"
to: "03def456...compressed_pubkey_hex"
timestampNonce: "01958d67-41c1-7dd4-8aa3-ef3575a84ab5"
```
## What Endorsements Do
* Split only the endorser's daily reward payout.
* Allocate payout shares in basis points (`ratioBps`).
* Keep any unallocated share with the endorser.
* Stay active until updated or deleted.
* Credit recipients in the same daily payout cycle.
If a recipient also earns their own rewards, payout history combines both values:
* `rewardsPayoutSats` for their own rewards.
* `endorsementsPayoutSats` for delegated rewards received.
* `payoutSats` as the total.
## Example
If an endorser configures:
* Recipient A: `3000` bps (30%)
* Recipient B: `2000` bps (20%)
And the endorser's payout for the day is `10000` sats:
* Recipient A receives `3000` sats as endorsements payout.
* Recipient B receives `2000` sats as endorsements payout.
* Endorser keeps `5000` sats as rewards payout.
## Security Model
Every create/update/delete request requires:
* A `timestampNonce` in UUIDv7 format.
* A secp256k1 signature by the endorser pubkey.
* The signature is over the SHA-256 hash of a canonical YAML payload.
Additional protections and constraints:
* Nonce timestamp must be within 5 minutes.
* Nonces are single-use (replay protection).
* You cannot endorse to yourself.
* Sum of all active ratios for one endorser cannot exceed `10000` bps.
## Endpoints
* `GET /v1/rewards/:pubkey/endorsements`
* `POST /v1/rewards/:pubkey/endorsements`
* `DELETE /v1/rewards/:pubkey/endorsements/:toPubkey`
For full request/response examples, see [Rewards API](/rewards/integrators/api#endorsements).
# FAQ
Source: https://docs.flashnet.xyz/usdb/faq
Common questions about USDB backing, reserves, rewards, and Brale
USDB is fully backed 1:1 by US Treasury bills and cash equivalents, held by regulated custodians. It's issued by [Brale](https://brale.xyz), a licensed stablecoin issuer.
Brale maintains reserves in:
* Short-duration US Treasury Bills (less than 3 months maturity)
* Cash at regulated U.S. financial institutions
Every USDB is redeemable for \$1 USD worth of these assets.
Brale is a regulated stablecoin-as-a-service provider based in the United States.
**Licensing:** Registered Money Services Business (MSB) with FinCEN (#31000257808337), holds Money Transmitter licenses in 44 U.S. states (NMLS ID #2376957), and is subject to quarterly state reporting and federal compliance requirements.
You can verify Brale's licenses yourself at [nmlsconsumeraccess.org](https://nmlsconsumeraccess.org).
Brale undergoes multiple layers of independent verification:
* **Monthly audits**: Third-party reserve attestations by Abdo accounting firm
* **Daily reporting**: Public self-attestations of reserves published daily
* **Annual audit**: Comprehensive financial review by a licensed CPA firm
* **Daily reconciliation**: Automated matching of on-chain token supply vs. fiat balances
Reserve attestations are publicly available on [brale.xyz](https://brale.xyz).
Flashnet funds rewards from protocol fees and other revenue. As volume grows, so does the revenue base that sustains rewards.
This system is baked into Flashnet's long-term strategy, and is not a promotional campaign. Rewards are paid daily in BTC and scale with your protocol usage.
**No.** You can buy, sell, transfer, or redeem your USDB at any time. Rewards
are calculated based on a rolling average and trading volume, but there's no
minimum holding period or withdrawal restrictions.
See the [Redeem USDB](/usdb/redeem) page for complete details on all exit
paths, including what's available now and what's coming soon.
10 USDB. Balances below 10 USDB don't earn rewards.
Daily. Rewards are calculated at midnight UTC and distributed to eligible
wallets.
USDB maintains its \$1 peg through full 1:1 backing by US Treasury bills and
cash equivalents. The value is secured by real assets held by regulated
custodians.
Yes. Create an account at [Brale](https://brale.xyz), complete their
verification process, and redeem USDB for USD (bank transfer) or USDC at a 1:1
ratio. See the [Redeem USDB](/usdb/redeem) page for step-by-step instructions.
Brale maintains multiple layers of protection:
**Legal structure:** Reserves are held in segregated accounts at regulated U.S. financial institutions. The structure is bankruptcy-remote, with reserves legally separate from Brale's operational funds. Reserves are used exclusively to back outstanding USDB.
**Security:**
* SOC 2 Type II certified (highest standards for security, availability, and confidentiality)
* Smart contracts independently audited by Certik
* Multi-party computation (MPC) for private key protection
Yes. USDB is a Spark-native token that can be held in any Spark wallet, transferred to any Spark address, and used in any Spark-compatible application.
Flashnet is just one place to trade and earn rewards on USDB.
Spark is a Bitcoin Layer 2 network that enables instant, low-cost transfers of BTC and tokens like USDB. It uses a FROST-based protocol to achieve sub-second settlement while maintaining self-custody.
Learn more at [spark.money](https://spark.money) or read the [Spark documentation](https://docs.spark.money).
# Get USDB
Source: https://docs.flashnet.xyz/usdb/get-usdb
Bridge USDC or swap BTC to acquire USDB on Spark
There are two ways to get USDB on Spark:
Convert USDC from Solana, Ethereum, Base, Arbitrum, Optimism, or Polygon to
USDB on Spark
Swap your BTC for USDB instantly on any supported venue
More integrations are planned, including native on- and off-ramps. [Contact us](mailto:partnerships@flashnet.xyz) if you want to integrate USDB.
***
## Bridge from USDC
Bridge USDC from another supported chain to get USDB.
Convert USDC to USDB on Spark.
Navigate to [usdb.flashnet.xyz/bridge](https://usdb.flashnet.xyz/bridge).
Choose your preferred network as your source chain.
Connect the wallet holding your USDC (Phantom, Coinbase Wallet, etc.).
Enter the amount of USDC you want to bridge.
Paste your Spark wallet address (starts with `spark1...`).
Approve the transaction in your wallet. USDB arrives in \~1-2 minutes.
### Supported Source Chains
| Chain | Token | Bridge time |
| -------- | ----- | ----------- |
| Solana | USDC | Few seconds |
| Ethereum | USDC | \~1 minute |
| Base | USDC | Few seconds |
| Arbitrum | USDC | Few seconds |
| Optimism | USDC | Few seconds |
| Polygon | USDC | Few seconds |
Bridge fees vary by route and network conditions. The bridge UI shows exact
fees before confirmation.
***
## Swap BTC for USDB
Swap BTC for USDB on any of these venues. Swaps settle instantly.
Pick any of the trading venues above.
Connect or create a Spark wallet.
Select the swap direction and enter your amount.
Check the price, fees, and slippage, then confirm.
| Swap details | |
| ------------------- | ------------------- |
| Settlement | Sub-second finality |
| Minimum | \$1 |
| Pricing | AMM liquidity pools |
| Slippage protection | Configurable limits |
### Fees
Spark network transfers are free (\$0 network fee).
Trading volume counts toward your [reward tier](/usdb/rewards). Higher volume
\= higher rewards.
***
## What You Need
Before getting USDB, make sure you have:
You need a Spark wallet to receive USDB. Use any Spark-compatible wallet like [Xverse](https://xverse.io), [Guap](https://useguap.com/), [Blitz Wallet](https://blitz-wallet.com/), [LayerZ](https://layerzwallet.com/), or [BitBit](https://bitbit.bot/).
Developers can also receive USDB directly via the Spark CLI or the [native Spark SDK](https://docs.spark.money/wallets/overview), without using a consumer wallet.
Either USDC on a supported chain (for bridging) or BTC on Spark (for swapping).
Your 24-hour trading volume determines your [reward tier](/usdb/rewards). A daily \$500 volume moves you to the 4% tier from the 3.5% base.
***
## After You Get USDB
Once you have USDB in your Spark wallet, you can:
* **Earn rewards**: Hold USDB to earn BTC rewards (3.5 - 6%)
* **Earn Flashpoints**: Accumulate points that boost your rewards
* **Trade**: Swap back to BTC anytime
* **Transfer**: Send USDB to any Spark address, free and instant
* **Redeem**: [Exit to BTC, USD, or USDC](/usdb/redeem) whenever you want
# Integrate USDB
Source: https://docs.flashnet.xyz/usdb/integrate
Add USDB support to wallets and apps with revenue sharing
Give your users BTC rewards. Keep a cut for yourself.
## For Your Users
* 3.5 - 6% in BTC
* Zero-friction stablecoin
* Instant settlement
* Self-custody
## For You
* Charge fees on swaps
* Earn volume bonuses
* No liquidity required
* Simple API integration
## Revenue Potential
At $10M monthly volume with 0.5% fee, earn $600k/year in integrator revenue.
| Fee at \$10M monthly volume | Integrator revenue |
| --------------------------- | ------------------ |
| 0.1% | \$120k/year |
| 0.25% | \$300k/year |
| 0.5% | \$600k/year |
## Integration Effort
| Integration | Time | Capabilities |
| ---------------- | --------- | ---------------------------- |
| Read-only | Minutes | Display balances and rewards |
| Swap Widget | \< 1 hour | Enable swaps, earn fees |
| Full Integration | \< 1 day | All revenue streams |
## Integration Models
* **Wallet Integration:** Display USDB balances, rewards, and Flashpoints in your wallet UI
* **Swap Integration:** Let users swap BTC ↔ USDB with your app taking a fee
* **Bridge Integration:** Offer USDC → USDB bridging with your branding
* **White-Label:** Full-featured USDB experience under your brand
## Who Should Integrate
| Integrator | Use case |
| ----------------- | ----------------------------------------------- |
| Bitcoin wallets | Give users a rewards-bearing stablecoin option |
| Lightning wallets | Instant BTC ↔ USD swaps |
| Exchanges | Add USDB as a trading pair |
| Payment apps | Dollar-denominated payments with BTC settlement |
| DeFi protocols | USDB as collateral or trading asset |
## What You Get
REST API for balances, rewards, Flashpoints, and leaderboards. Full Swagger
documentation.
Track your fees earned and performance metrics.
Direct access to the Flashnet team for integration questions.
Featured placement in Flashnet materials for launch partners.
## Contact
[partnerships@flashnet.xyz](mailto:partnerships@flashnet.xyz)
# What is USDB?
Source: https://docs.flashnet.xyz/usdb/overview
Dollar-backed stablecoin on Spark with 3.5-6% BTC rewards paid daily
USDB is a dollar-backed stablecoin on [Spark](https://docs.spark.money). Hold USDB and earn 3.5 - 6% rewards paid in BTC daily.
## Why USDB
* **3.5 - 6% in BTC:** Hold 10+ USDB and earn rewards paid in BTC daily.
* **Dollar Stability:** No exposure to BTC volatility
* **Instant Settlement:** Sub-second transfers on Spark
* **Zero Fees:** Spark-to-Spark transfers are free
## How USDB is Backed
USDB is issued by [Brale](https://brale.xyz), a regulated US-based stablecoin issuer. Every USDB is fully backed 1:1 by:
* **US Treasury Bills:** Short-term government securities
* **Cash Equivalents:** Held at regulated financial institutions
USDB follows the same fully-reserved backing model as USDC and USDT. Users who onboard through Brale can redeem USDB for USD at a 1:1 ratio at any time.
## USDB vs Other Stablecoins
### USDB vs USDC
| Feature | USDB | USDC |
| ------------- | ------------------ | ---------------------- |
| Chain | Spark (Bitcoin L2) | Ethereum, Solana, etc. |
| Settlement | Sub-second | Network dependent |
| Transfer fees | Free on Spark | Gas fees |
| BTC rewards | 3.5 - 6% | No |
### USDB vs USDT
| Feature | USDB | USDT |
| ------------- | ------------------ | -------------------- |
| Chain | Spark (Bitcoin L2) | Ethereum, Tron, etc. |
| Settlement | Sub-second | Network dependent |
| Transfer fees | Free on Spark | Gas fees |
| BTC rewards | 3.5 - 6% | No |
## Token Details
| Detail | Value |
| ---------- | ----- |
| Token Name | USDB |
| Decimals | 6 |
| Issuer | Brale |
**Token Address:** [`btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87`](https://sparkscan.io/token/btkn1xgrvjwey5ngcagvap2dzzvsy4uk8ua9x69k82dwvt5e7ef9drm9qztux87?network=mainnet)
# Redeem USDB
Source: https://docs.flashnet.xyz/usdb/redeem
Convert USDB to BTC or USDC with Flashnet.
Use [Flashnet Trade](https://trade.flashnet.xyz) to convert USDB to BTC or USDC.
Choose USDB as the input asset, select your destination asset and network, and review the quote before confirming.
# Rewards
Source: https://docs.flashnet.xyz/usdb/rewards
BTC reward tiers and daily payout mechanics for USDB holders
Hold USDB and earn BTC rewards paid daily to your wallet.
## How Rewards Work
Every USDB holder earns rewards in BTC. Your reward rate depends on your 24-hour trading volume.
* **Hold USDB**: 3.5% - 6% in BTC
* **Trade More**: Higher trading volume = higher reward rate
* **Collect Daily**: BTC deposited to your wallet at 01:00 UTC
## Reward Tiers
Your 24-hour trading volume determines your reward rate:
| 24-hour volume | Reward rate |
| -------------- | ----------- |
| \$0 | 3.5% |
| \$500+ | 4.0% |
| \$2,500+ | 4.5% |
| \$5,000+ | 5.0% |
| \$10,000+ | 5.5% |
| \$25,000+ | 6.0% |
Volume is measured in 24-hour rolling windows. Your rate updates continuously
as you trade.
## Reward Examples
Static examples of daily and monthly BTC rewards, assuming BTC at approximately \$100,000, a constant USDB balance and reward rate, and a 30-day month. Daily and monthly amounts are rounded separately.
### \$1,000 USDB balance
| 24-hour volume (rate) | Daily (sats) | Monthly (sats) |
| --------------------- | ------------ | -------------- |
| \$0 (3.5%) | 96 | 2,877 |
| \$500 (4.0%) | 110 | 3,288 |
### \$10,000 USDB balance
| 24-hour volume (rate) | Daily (sats) | Monthly (sats) |
| --------------------- | ------------ | -------------- |
| \$0 (3.5%) | 959 | 28,767 |
| \$2,500 (4.5%) | 1,233 | 36,986 |
### \$50,000 USDB balance
| 24-hour volume (rate) | Daily (sats) | Monthly (sats) |
| --------------------- | ------------ | -------------- |
| \$5,000 (5.0%) | 6,849 | 205,479 |
### \$100,000 USDB balance
| 24-hour volume (rate) | Daily (sats) | Monthly (sats) |
| --------------------- | ------------ | -------------- |
| \$25,000 (6.0%) | 16,438 | 493,151 |
## Requirements
* **Minimum balance**: 10 USDB to earn rewards
* **Maximum balance**: 500,000 USDB (rewards capped at this amount)
* **Payout timing**: Daily at 01:00 UTC
* **Payout currency**: BTC (deposited to your Spark wallet)
## How Rewards Are Calculated
Your daily reward is based on your **Time-Weighted Average Balance (TWAB)**, not a snapshot.
For example, if you hold 1,000 USDB for 12 hours then 2,000 USDB for 12 hours, your TWAB is 1,500 USDB. This prevents gaming by depositing right before the daily cutoff.
**Daily reward formula:**
```text theme={null}
daily_reward = (TWAB * reward_rate) / 365
```
The reward is converted to BTC at the current exchange rate and deposited to your wallet.
For the constant-balance examples above:
```text theme={null}
daily_btc = (usdb_balance * rewards_rate / 365) / btc_price
```
## Quick Start
[Bridge USDC or swap BTC](/usdb/get-usdb) for USDB.
Your USDB automatically earns rewards. No staking required.
Trade to reach higher reward tiers and earn more.
BTC rewards deposited to your wallet daily.
Flashnet reserves the right to remove users suspected of sybil attacks or
other abuse from the rewards program.
# Transparency
Source: https://docs.flashnet.xyz/usdb/transparency
Where USDB rewards come from and why they're sustainable
Flashnet pays BTC rewards from its own revenue. No rehypothecation, no lending, no fractional reserves. Flashnet doesn't even issue USDB. Brale does.
Brale is a US-regulated stablecoin issuer. See [Who is Brale?](/usdb/faq#who-is-brale) for details.
## Two Separate Companies
**Brale** issues USDB and maintains the reserves. You hold their stablecoin.
**Flashnet** operates the protocol and shares revenue with USDB holders. You use their protocol.
Independent companies, working together.
## Where Rewards Come From
Flashnet generates revenue from protocol activity and converts a portion to BTC for daily distribution to USDB holders.
Your reward rate (3.5 - 6%) scales with protocol usage. More usage, higher rate.
## Principal Safety
Your USDB is backed 1:1 by T-bills and cash equivalents held at regulated custodians. Flashnet never touches those reserves. If Flashnet disappeared tomorrow, your USDB would still be redeemable through Brale.
## Reward Sustainability
Rewards scale with protocol activity. High usage means more revenue and higher rewards. Low usage means lower rewards. The system balances itself.
## Growth Strategy
USDB rewards are a growth investment:
1. USDB holders bring liquidity
2. More liquidity improves execution
3. Better execution attracts users
4. More users generate more revenue
5. More revenue funds more rewards
Rewards cost Flashnet money today to generate more revenue tomorrow. Uber subsidized rides to build network density. Banks offer sign-up bonuses to acquire customers. Flashnet pays BTC rewards to grow protocol usage. The difference is that these rewards are built into the protocol indefinitely.
## Brale's Role
Brale runs a standard stablecoin business:
1. You give Brale \$1
2. Brale gives you 1 USDB
3. Brale holds your \$1 in T-bills at regulated custodians
4. When you redeem, Brale returns \$1
This is how USDC and USDT work. Flashnet's rewards are separate from this backing mechanism.
Check out the full FAQ