{"openapi":"3.0.0","info":{"version":"1.0.0","title":"ShapeShift Public API","description":"The ShapeShift Public API lets you integrate multi-chain swap functionality into your application. Fetch rates from multiple DEX aggregators and bridges, build executable quotes, and track swaps across supported blockchains.\n\n## Base URL\n\n```\nhttps://api.shapeshift.com\n```\n\nAll endpoints are versioned under `/v1` (e.g. `https://api.shapeshift.com/v1/swap/rates`). This interactive reference is served at `https://api.shapeshift.com/docs`, and the raw OpenAPI document at `https://api.shapeshift.com/docs/json`.\n\n## Two ways to integrate\n\n1. **Swap Widget SDK** — a drop-in React component with built-in UI, wallet connection, and multi-chain support. The fastest path. See the **Swap Widget SDK** section.\n2. **REST API** — call the endpoints directly and build your own UI for full control over UX. See the **REST API Guide** section, then the per-endpoint reference below.\n\n## Affiliate tracking (optional)\n\nSend an `X-Partner-Code` header with your registered partner code (e.g. `your-partner-code`) on the swap endpoints to attribute swaps for affiliate revenue share. The API attributes the swap to your affiliate account and applies your configured fee (bps) automatically. All endpoints work without it — unattributed swaps use the default fee. See the [Affiliate Program guide](https://github.com/shapeshift/web/blob/develop/docs/affiliates.md) for how to obtain a code.\n\n## Asset IDs (CAIP-19)\n\nAssets are identified with [CAIP-19](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-19.md): `{chainId}/{assetNamespace}:{assetReference}`\n\n- Native ETH: `eip155:1/slip44:60`\n- USDC on Ethereum: `eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48`\n- Native BTC: `bip122:000000000019d6689c085ae165831e93/slip44:0`\n\nChains use [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md) (e.g. `eip155:1`). Use `GET /v1/chains` and `GET /v1/assets` to discover supported values.\n\n## Errors\n\nErrors return the appropriate HTTP status and a JSON body:\n\n```json\n{ \"error\": \"Human-readable message\", \"code\": \"MACHINE_CODE\", \"details\": [] }\n```\n\n`code` and `details` are present where applicable (e.g. `QUOTE_NOT_FOUND`, `TX_HASH_REQUIRED`, `TX_HASH_MISMATCH`, `RATE_LIMIT_EXCEEDED`, validation `details`).\n\n## Rate limiting\n\nEndpoints are rate limited per IP on a fixed 60-second window. A global limit applies across all endpoints, and individual endpoint groups (data, rates, quote, status, affiliate) have their own independent limits on top of it — so a request counts against both. When either is exceeded, the API returns `429` with code `RATE_LIMIT_EXCEEDED` and these headers:\n\n- `Retry-After` — seconds until the window resets\n- `RateLimit-Limit` — max requests allowed per window\n- `RateLimit-Remaining` — requests remaining in the current window\n- `RateLimit-Reset` — seconds until the window resets\n\nBack off using `Retry-After` and avoid polling faster than necessary (see the REST API Guide for polling guidance).\n"},"servers":[{"url":"https://api.shapeshift.com"},{"url":"http://localhost:3001"}],"components":{"schemas":{"RateLimitError":{"type":"object","properties":{"error":{"type":"string","example":"Too many requests, please try again later"},"code":{"type":"string","enum":["RATE_LIMIT_EXCEEDED"],"example":"RATE_LIMIT_EXCEEDED"}},"required":["error","code"]},"Asset":{"type":"object","properties":{"assetId":{"type":"string","example":"eip155:1/slip44:60"},"chainId":{"type":"string","example":"eip155:1"},"name":{"type":"string","example":"Ethereum"},"symbol":{"type":"string","example":"ETH"},"precision":{"type":"number","example":18},"color":{"type":"string","example":"#5C6BC0"},"icon":{"type":"string","example":"https://assets.coincap.io/assets/icons/eth@2x.png"},"networkName":{"type":"string","example":"Ethereum"},"networkColor":{"type":"string","example":"#5C6BC0"},"networkIcon":{"type":"string","example":"https://assets.coincap.io/assets/icons/eth@2x.png"},"explorer":{"type":"string","example":"https://etherscan.io"},"explorerAddressLink":{"type":"string","example":"https://etherscan.io/address/"},"explorerTxLink":{"type":"string","example":"https://etherscan.io/tx/"},"relatedAssetKey":{"type":"string","nullable":true}},"required":["assetId","chainId","name","symbol","precision","color","icon","explorer","explorerAddressLink","explorerTxLink","relatedAssetKey"]},"AffiliateConfig":{"type":"object","properties":{"id":{"type":"string","example":"abc123"},"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"receiveAddress":{"type":"string","nullable":true,"pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"partnerCode":{"type":"string","example":"mypartner"},"partnerBps":{"type":"number","example":50},"shapeshiftBps":{"type":"number","example":10},"isActive":{"type":"boolean","example":true},"createdAt":{"type":"string","format":"date-time","example":"2024-01-01T00:00:00.000Z"},"updatedAt":{"type":"string","format":"date-time","example":"2024-01-01T00:00:00.000Z"},"role":{"type":"string","enum":["owner","admin"],"example":"owner"}},"required":["id","walletAddress","receiveAddress","partnerCode","partnerBps","shapeshiftBps","isActive","createdAt","updatedAt"]},"AffiliateMember":{"type":"object","properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"role":{"type":"string","enum":["owner","admin"],"example":"owner"},"addedBy":{"type":"string","nullable":true,"pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"createdAt":{"type":"string","format":"date-time","example":"2024-01-01T00:00:00.000Z"}},"required":["walletAddress","role","addedBy","createdAt"]},"AffiliateMembers":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/AffiliateMember"}}},"required":["members"]},"AffiliateSwap":{"type":"object","properties":{"swapId":{"type":"string","example":"swap-uuid-1234"},"status":{"type":"string","example":"completed"},"sellAsset":{"$ref":"#/components/schemas/Asset"},"buyAsset":{"$ref":"#/components/schemas/Asset"},"sellAmountCryptoPrecision":{"type":"string","example":"1.0"},"sellAmountUsd":{"type":"string","nullable":true,"example":"1234.56"},"buyAmountCryptoPrecision":{"type":"string","nullable":true,"example":"948.0"},"buyAmountUsd":{"type":"string","nullable":true,"example":"1234.56"},"affiliateFeeAmountUsd":{"type":"string","nullable":true,"example":"3.70"},"affiliateFeeStatus":{"type":"string","enum":["earned","pending","review","failed"],"description":"earned: the verified on-chain fee that will be paid. pending: still settling, fee is an estimate. review: verified but held for review, fee is an estimate. failed: will not be paid, no fee","example":"earned"},"affiliateFeeStatusReason":{"type":"string","nullable":true,"enum":["swap-pending","verification-pending","attribution-pending","no-partner-share","fee-under-review","swap-failed","not-attributed","fee-not-verified","no-affiliate-fee","fee-waived"],"description":"Why the fee is pending, under review or not paid; null once earned","example":null},"affiliateBps":{"type":"integer","minimum":0,"example":60},"partnerBps":{"type":"integer","nullable":true,"minimum":0,"example":20},"shapeshiftBps":{"type":"integer","minimum":0,"example":10},"swapperName":{"type":"string","example":"THORChain"},"sellTxHash":{"type":"string","nullable":true,"example":"0xabc123"},"buyTxHash":{"type":"string","nullable":true,"example":"0xdef456"},"isAffiliateVerified":{"type":"boolean","nullable":true,"example":true},"createdAt":{"type":"string","example":"2024-01-01T00:00:00.000Z"}},"required":["swapId","status","sellAsset","buyAsset","sellAmountCryptoPrecision","sellAmountUsd","buyAmountCryptoPrecision","buyAmountUsd","affiliateFeeAmountUsd","affiliateFeeStatus","affiliateFeeStatusReason","affiliateBps","partnerBps","shapeshiftBps","swapperName","sellTxHash","buyTxHash","isAffiliateVerified","createdAt"]},"AffiliateSwapsResponse":{"type":"object","properties":{"swaps":{"type":"array","items":{"$ref":"#/components/schemas/AffiliateSwap"}},"nextCursor":{"type":"string","nullable":true,"example":"swap-uuid-1234"}},"required":["swaps","nextCursor"]},"AffiliateStatsResponse":{"type":"object","properties":{"totalSwaps":{"type":"number","example":42},"totalVolumeUsd":{"type":"string","example":"12345.67"},"totalFeesEarnedUsd":{"type":"string","example":"44.44"}},"required":["totalSwaps","totalVolumeUsd","totalFeesEarnedUsd"]},"SiweNonceResponse":{"type":"object","properties":{"nonce":{"type":"string","example":"abcdef123456"}},"required":["nonce"]},"SiweVerifyResponse":{"type":"object","properties":{"token":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"address":{"type":"string","example":"0x1234567890123456789012345678901234567890"}},"required":["token","address"]},"Chain":{"type":"object","properties":{"chainId":{"type":"string","example":"eip155:1"},"name":{"type":"string","example":"Ethereum"},"type":{"type":"string","enum":["evm","utxo","cosmos","solana","tron","sui","near","starknet","ton","aptos"],"example":"evm"},"symbol":{"type":"string","example":"ETH"},"precision":{"type":"number","example":18},"color":{"type":"string","example":"#5C6BC0"},"networkColor":{"type":"string","example":"#5C6BC0"},"icon":{"type":"string","example":"https://rawcdn.githack.com/trustwallet/assets/32e51d582a890b3dd3135fe3ee7c20c2fd699a6d/blockchains/ethereum/info/logo.png"},"networkIcon":{"type":"string","example":"https://rawcdn.githack.com/trustwallet/assets/32e51d582a890b3dd3135fe3ee7c20c2fd699a6d/blockchains/ethereum/info/logo.png"},"explorer":{"type":"string","example":"https://etherscan.io"},"explorerAddressLink":{"type":"string","example":"https://etherscan.io/address/"},"explorerTxLink":{"type":"string","example":"https://etherscan.io/tx/"},"nativeAssetId":{"type":"string","example":"eip155:1/slip44:60"},"isSellSupported":{"type":"boolean","description":"Whether assets on this chain can be sold. Chains without it are destination-only - rates and quotes reject them as a sell asset.","example":true}},"required":["chainId","name","type","symbol","precision","color","explorer","explorerAddressLink","explorerTxLink","nativeAssetId","isSellSupported"]},"PartnerResolution":{"type":"object","properties":{"partnerAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"partnerBps":{"type":"integer","minimum":0,"example":50},"partnerCode":{"type":"string","example":"mypartner"},"shapeshiftBps":{"type":"integer","minimum":0,"example":10}},"required":["partnerAddress","partnerBps","partnerCode","shapeshiftBps"]},"QuoteStep":{"type":"object","properties":{"sellAsset":{"$ref":"#/components/schemas/Asset"},"buyAsset":{"$ref":"#/components/schemas/Asset"},"sellAmountCryptoBaseUnit":{"type":"string","example":"1000000000000000000"},"buyAmountAfterFeesCryptoBaseUnit":{"type":"string","example":"995000000"},"allowanceContract":{"type":"string","example":"0xdef1c0ded9bec7f1a1670819833240f027b25eff"},"estimatedExecutionTimeMs":{"type":"number","example":60000},"source":{"type":"string","example":"0x"},"transactionData":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["evm"],"example":"evm"},"chainId":{"type":"number","example":1},"to":{"type":"string","example":"0xdef1c0ded9bec7f1a1670819833240f027b25eff"},"data":{"type":"string","example":"0x..."},"value":{"type":"string","example":"1000000000000000000"},"gasLimit":{"type":"string","example":"300000"},"signatureRequired":{"type":"object","properties":{"type":{"type":"string","enum":["zrx_permit2"]},"eip712":{"type":"object","additionalProperties":{"nullable":true}}},"required":["type","eip712"],"description":"When present, the transaction requires a permit2 signature before broadcast: sign the eip712 payload with eth_signTypedData_v4, then append a 32-byte big-endian signature-length word followed by the signature to `data` (the 0x settler convention). The supplied gasLimit already accounts for the appended signature."}},"required":["type","chainId","to","data","value"]},{"type":"object","properties":{"type":{"type":"string","enum":["solana_instructions"],"example":"solana_instructions"},"instructions":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"string"},"keys":{"type":"array","items":{"type":"object","properties":{"pubkey":{"type":"string"},"isSigner":{"type":"boolean"},"isWritable":{"type":"boolean"}},"required":["pubkey","isSigner","isWritable"]}},"data":{"type":"string"}},"required":["programId","keys","data"]}},"addressLookupTableAddresses":{"type":"array","items":{"type":"string"}}},"required":["type","instructions","addressLookupTableAddresses"]},{"type":"object","properties":{"type":{"type":"string","enum":["solana_serialized_tx"],"example":"solana_serialized_tx"},"serializedTx":{"type":"string"}},"required":["type","serializedTx"]},{"type":"object","properties":{"type":{"type":"string","enum":["utxo"],"example":"utxo"},"to":{"type":"string"},"opReturnData":{"type":"string"},"value":{"type":"string"}},"required":["type","to","value"]},{"type":"object","properties":{"type":{"type":"string","enum":["cosmossdk_msg_send"],"example":"cosmossdk_msg_send"},"chainId":{"type":"string"},"to":{"type":"string"},"denom":{"type":"string","example":"uatom"},"value":{"type":"string"},"memo":{"type":"string"}},"required":["type","chainId","to","denom","value"]},{"type":"object","properties":{"type":{"type":"string","enum":["cosmossdk_msg_deposit"],"example":"cosmossdk_msg_deposit"},"chainId":{"type":"string"},"value":{"type":"string"},"memo":{"type":"string"},"coin":{"type":"string","example":"THOR.RUNE"}},"required":["type","chainId","value","memo","coin"]},{"type":"object","properties":{"type":{"type":"string","enum":["tron"],"example":"tron"},"to":{"type":"string","example":"TCFNp179Lg46D16zKoumd4Poa2WFFdtqYj"},"value":{"type":"string","example":"1000000"},"data":{"type":"string","description":"Contract call data; absent for a plain transfer of the sell asset"},"memo":{"type":"string"}},"required":["type","to","value"]}]}},"required":["sellAsset","buyAsset","sellAmountCryptoBaseUnit","buyAmountAfterFeesCryptoBaseUnit","allowanceContract","source"]},"QuoteResponse":{"type":"object","properties":{"quoteId":{"type":"string","format":"uuid"},"swapperName":{"type":"string","example":"0x"},"rate":{"type":"string","example":"0.995"},"sellAsset":{"$ref":"#/components/schemas/Asset"},"buyAsset":{"$ref":"#/components/schemas/Asset"},"sellAmountCryptoBaseUnit":{"type":"string"},"buyAmountBeforeFeesCryptoBaseUnit":{"type":"string"},"buyAmountAfterFeesCryptoBaseUnit":{"type":"string"},"affiliateBps":{"type":"string","description":"Total on-chain fee in bps.","example":"60"},"partnerBps":{"type":"string","description":"Partner share in bps.","example":"50"},"shapeshiftBps":{"type":"string","description":"ShapeShift platform fee in bps.","example":"10"},"slippageTolerancePercentageDecimal":{"type":"string","example":"0.01"},"networkFeeCryptoBaseUnit":{"type":"string","example":"23000"},"approval":{"type":"object","properties":{"isRequired":{"type":"boolean","example":true},"spender":{"type":"string","example":"0xdef1c0ded9bec7f1a1670819833240f027b25eff"},"approvalTxs":{"type":"array","items":{"type":"object","properties":{"to":{"type":"string","example":"0xdac17f958d2ee523a2206206994597c13d831ec7"},"data":{"type":"string","example":"0x095ea7b3..."},"value":{"type":"string","example":"0"}},"required":["to","data","value"]},"description":"Ready-to-sign approval transactions in broadcast order, empty when the current allowance already covers the amount. Approvals are exact - sized to the step's sellAmountCryptoBaseUnit and consumed by the swap's execution. Usually a single approve; tokens that require resetting a non-zero allowance before changing it (e.g. USDT) get a preceding approve(spender, 0). Sign and broadcast sequentially, waiting for each to confirm. Clients preferring an unlimited approval can build their own approve to `spender` instead."}},"required":["isRequired","spender","approvalTxs"]},"steps":{"type":"array","items":{"$ref":"#/components/schemas/QuoteStep"}},"depositAddress":{"type":"string","description":"Present when this quote can be paid by a plain transfer from any wallet: send exactly sellAmountCryptoBaseUnit here before expiresAt, then poll /v1/swap/status with quoteId alone. A deposit sent after expiresAt may be refunded to sendAddress, or lost. Absent means the swap must be signed by the wallet at sendAddress.","example":"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"},"expiresAt":{"type":"number","description":"Epoch ms after which the quote must not be executed - the swapper's own deadline. Broadcasting after it risks a failed swap, or lost funds on an externally paid quote. Request a fresh quote instead.","example":1754265600000}},"required":["quoteId","swapperName","rate","sellAsset","buyAsset","sellAmountCryptoBaseUnit","buyAmountBeforeFeesCryptoBaseUnit","buyAmountAfterFeesCryptoBaseUnit","affiliateBps","shapeshiftBps","approval","steps","expiresAt"]},"RateResponse":{"type":"object","properties":{"rates":{"type":"array","items":{"type":"object","properties":{"swapperName":{"type":"string"},"rate":{"type":"string"},"buyAmountCryptoBaseUnit":{"type":"string"},"sellAmountCryptoBaseUnit":{"type":"string"},"steps":{"type":"number"},"allowanceContract":{"type":"string","description":"First-hop approval spender for the sell token. Non-empty means executing this swapper pulls the sell token from an approved allowance - clients wanting to check or set an allowance manually before quoting can use it directly. Empty or absent means no approval is involved.","example":"0xdef1c0ded9bec7f1a1670819833240f027b25eff"},"estimatedExecutionTimeMs":{"type":"number"},"supportsExternalPayment":{"type":"boolean","description":"True when this swap can be paid externally - the sell asset is sent to a provider-issued deposit address, so any wallet can fund it instead of the requesting client signing a transaction. Advisory only - the quote is authoritative: pay the address in its depositAddress field, and if the quote omits it, the route needs a connected wallet after all.","example":true},"priceImpactPercentageDecimal":{"type":"string"},"affiliateBps":{"type":"string","description":"Total on-chain fee in bps.","example":"60"},"partnerBps":{"type":"string","description":"Partner share in bps.","example":"50"},"shapeshiftBps":{"type":"string","description":"ShapeShift platform fee in bps.","example":"10"},"networkFeeCryptoBaseUnit":{"type":"string"},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["swapperName","rate","buyAmountCryptoBaseUnit","sellAmountCryptoBaseUnit","steps","supportsExternalPayment","affiliateBps","shapeshiftBps"]}},"timestamp":{"type":"number"},"expiresAt":{"type":"number"}},"required":["rates","timestamp","expiresAt"]},"SwapStatusResponse":{"type":"object","properties":{"quoteId":{"type":"string","format":"uuid"},"txHash":{"type":"string"},"status":{"type":"string","enum":["pending","submitted","confirmed","failed"]},"swapperName":{"type":"string"},"sellAssetId":{"type":"string"},"buyAssetId":{"type":"string"},"sellAmountCryptoBaseUnit":{"type":"string"},"buyAmountAfterFeesCryptoBaseUnit":{"type":"string"},"partnerAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"affiliateBps":{"type":"string","description":"Total on-chain fee in bps.","example":"60"},"partnerBps":{"type":"string","description":"Partner share in bps.","example":"50"},"shapeshiftBps":{"type":"string","description":"ShapeShift platform fee in bps.","example":"10"},"registeredAt":{"type":"number"},"buyTxHash":{"type":"string"},"txLink":{"type":"string"},"buyTxLink":{"type":"string"},"swapperTxLink":{"type":"string"},"isAffiliateVerified":{"type":"boolean"}},"required":["quoteId","status","swapperName","sellAssetId","buyAssetId","sellAmountCryptoBaseUnit","buyAmountAfterFeesCryptoBaseUnit","affiliateBps","shapeshiftBps"]}},"parameters":{}},"paths":{"/v1/affiliate":{"post":{"operationId":"createAffiliate","summary":"Create affiliate","description":"Register a new affiliate. Requires a valid SIWE JWT in the Authorization header.","tags":["Affiliate"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"receiveAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"partnerCode":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9]+$"},"bps":{"type":"integer","minimum":0,"maximum":1000}},"required":["walletAddress","partnerCode","bps"]}}}},"responses":{"201":{"description":"Affiliate created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateConfig"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"409":{"description":"Affiliate already exists"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/affiliate/{address}":{"get":{"operationId":"getAffiliate","summary":"Get affiliate config","description":"Retrieve affiliate configuration for a wallet address. The wallet may be the owner or an admin; the response role says which.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"address","in":"path"}],"responses":{"200":{"description":"Affiliate configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateConfig"}}}},"400":{"description":"Invalid request parameters"},"404":{"description":"Affiliate not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}},"patch":{"operationId":"updateAffiliate","summary":"Update affiliate","description":"Update an existing affiliate configuration. Requires a valid SIWE JWT in the Authorization header.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"address","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"receiveAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"bps":{"type":"integer","minimum":0,"maximum":1000}}}}}},"responses":{"200":{"description":"Updated affiliate configuration","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateConfig"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Affiliate not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/affiliate/stats":{"get":{"operationId":"getAffiliateStats","summary":"Get affiliate statistics","description":"Retrieve aggregated swap statistics for an affiliate by partnerCode. Returns total swaps, volume, and fees earned. Supports optional date range filtering.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9]+$"},"required":true,"name":"partnerCode","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"endDate","in":"query"}],"responses":{"200":{"description":"Affiliate statistics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateStatsResponse"}}}},"400":{"description":"Invalid request parameters"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/affiliate/swaps":{"get":{"operationId":"getAffiliateSwaps","summary":"Get affiliate swaps","description":"Retrieve paginated swap history for an affiliate by partnerCode. Supports optional date range filtering.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","minLength":3,"maxLength":32,"pattern":"^[a-z0-9]+$"},"required":true,"name":"partnerCode","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"startDate","in":"query"},{"schema":{"type":"string","format":"date-time"},"required":false,"name":"endDate","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"Affiliate swaps","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateSwapsResponse"}}}},"400":{"description":"Invalid query parameters"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/affiliate/{address}/members":{"get":{"operationId":"getAffiliateMembers","summary":"List affiliate members","description":"List the owner and admin wallets of an affiliate. Requires a valid SIWE JWT in the Authorization header whose address equals {address}; that wallet may be the owner or any admin of the affiliate.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"address","in":"path"}],"responses":{"200":{"description":"Owner and admins of the affiliate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateMembers"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Affiliate or member not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}},"post":{"operationId":"addAffiliateMember","summary":"Add affiliate admin","description":"Grant another wallet admin access to the affiliate. Requires a valid SIWE JWT in the Authorization header whose address equals {address}; that wallet may be the owner or any admin of the affiliate.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"address","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"walletAddress":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"}},"required":["walletAddress"]}}}},"responses":{"200":{"description":"Owner and admins of the affiliate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateMembers"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Affiliate or member not found"},"409":{"description":"Wallet already belongs to an affiliate"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/affiliate/{address}/members/{memberAddress}":{"delete":{"operationId":"removeAffiliateMember","summary":"Remove affiliate admin","description":"Revoke a wallet's admin access to the affiliate. Requires a valid SIWE JWT in the Authorization header whose address equals {address}; that wallet may be the owner or any admin of the affiliate.","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"address","in":"path"},{"schema":{"type":"string","pattern":"^0x[0-9a-fA-F]{40}$","example":"0x1234567890123456789012345678901234567890"},"required":true,"name":"memberAddress","in":"path"}],"responses":{"200":{"description":"Owner and admins of the affiliate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffiliateMembers"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Affiliate or member not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/assets":{"get":{"operationId":"listAssets","summary":"List supported assets","description":"Get a list of all supported assets, optionally filtered by chain.","tags":["Supported Assets"],"parameters":[{"schema":{"type":"string","example":"eip155:1"},"required":false,"name":"chainId","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":1000,"default":100,"example":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":"number","nullable":true,"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"List of assets","content":{"application/json":{"schema":{"type":"object","properties":{"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"timestamp":{"type":"number"}},"required":["assets","timestamp"]}}}},"400":{"description":"Invalid query parameters"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/assets/{assetId}":{"get":{"operationId":"getAssetById","summary":"Get asset by ID","description":"Get details of a specific asset by its ID (URL encoded).","tags":["Supported Assets"],"parameters":[{"schema":{"type":"string","minLength":1,"example":"eip155:1/slip44:60"},"required":true,"name":"assetId","in":"path"}],"responses":{"200":{"description":"Asset details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Asset"}}}},"400":{"description":"Invalid asset ID"},"404":{"description":"Asset not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/assets/count":{"get":{"operationId":"getAssetCount","summary":"Get asset count","description":"Get the total number of supported assets, optionally filtered by chain.","tags":["Supported Assets"],"parameters":[{"schema":{"type":"string","example":"eip155:1"},"required":false,"name":"chainId","in":"query"}],"responses":{"200":{"description":"Asset count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","example":5000},"timestamp":{"type":"number"}},"required":["count","timestamp"]}}}},"400":{"description":"Invalid query parameters"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/auth/siwe/nonce":{"post":{"operationId":"siweNonce","summary":"Get SIWE nonce","description":"Request a nonce for Sign-In with Ethereum (SIWE) authentication.","tags":["Auth"],"responses":{"200":{"description":"SIWE nonce","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiweNonceResponse"}}}},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/auth/siwe/verify":{"post":{"operationId":"siweVerify","summary":"Verify SIWE signature","description":"Verify a Sign-In with Ethereum (SIWE) message and signature. Returns a JWT token on success.","tags":["Auth"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"signature":{"type":"string"}},"required":["message","signature"]}}}},"responses":{"200":{"description":"Authentication successful","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiweVerifyResponse"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Invalid signature"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/chains":{"get":{"operationId":"listChains","summary":"List supported chains","description":"Get a list of all supported blockchain networks, sorted alphabetically by name.","tags":["Supported Chains"],"responses":{"200":{"description":"List of chains","content":{"application/json":{"schema":{"type":"object","properties":{"chains":{"type":"array","items":{"$ref":"#/components/schemas/Chain"}},"timestamp":{"type":"number"}},"required":["chains","timestamp"]}}}},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/chains/count":{"get":{"operationId":"getChainCount","summary":"Get chain count","description":"Get the total number of supported blockchain networks.","tags":["Supported Chains"],"responses":{"200":{"description":"Chain count","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number","example":28},"timestamp":{"type":"number"}},"required":["count","timestamp"]}}}},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/partner/{code}":{"get":{"operationId":"resolvePartner","summary":"Resolve a partner referral code","description":"Resolve a partner referral code to its attribution details (partner address and bps split).","tags":["Affiliate"],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"code","in":"path"}],"responses":{"200":{"description":"Partner attribution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerResolution"}}}},"400":{"description":"Invalid request parameters"},"404":{"description":"Partner code not found"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}},"/v1/swap/quote":{"post":{"operationId":"getSwapQuote","summary":"Get executable quote","description":"Get an executable quote for a swap, including transaction data. Requires a specific swapper name.","tags":["Swaps"],"parameters":[{"schema":{"type":"string"},"required":false,"description":"Partner code for affiliate fee attribution. The API resolves the code to the registered affiliate address and BPS. Register a code at the affiliate dashboard.","name":"X-Partner-Code","in":"header","example":"vultisig"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sellAssetId":{"type":"string","minLength":1,"example":"eip155:1/slip44:60"},"buyAssetId":{"type":"string","minLength":1,"example":"bip122:000000000019d6689c085ae165831e93/slip44:0"},"sellAmountCryptoBaseUnit":{"type":"string","pattern":"^\\d+$","description":"Exact amount of the sell asset to send, in base units. Required unless buyAmountCryptoBaseUnit is given.","example":"1000000000000000000"},"buyAmountCryptoBaseUnit":{"type":"string","pattern":"^(?!0+$)\\d+$","description":"Exact amount of the buy asset to receive, in base units. The sell amount you must send is derived from it and returned on the quote. Mutually exclusive with sellAmountCryptoBaseUnit, and rejected for swappers that cannot quote an exact output.","example":"100000"},"receiveAddress":{"type":"string","minLength":1,"example":"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq"},"sendAddress":{"type":"string","minLength":1,"description":"The user's address on the sell chain: the swap is funded from it, and refunds return to it. When the quote carries a depositAddress nothing is sent from it - the deposit can arrive from any wallet - so it only receives refunds, and must still be an address the user controls. For UTXO chains, use the account's first receive address (m/84'/0'/0'/0/0).","example":"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"},"swapperName":{"type":"string","minLength":1,"example":"Relay"},"slippageTolerancePercentageDecimal":{"type":"string","example":"0.01"},"accountNumber":{"type":"number","nullable":true,"default":0,"example":0},"xpub":{"type":"string","example":"zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs"}},"required":["sellAssetId","buyAssetId","receiveAddress","sendAddress","swapperName"]}}}},"responses":{"200":{"description":"Swap quote","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"400":{"description":"Invalid request or unavailable swapper"},"404":{"description":"No quote available"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"502":{"description":"Swapper returned an expired or implausible quote deadline"}}}},"/v1/swap/rates":{"get":{"operationId":"getSwapRates","summary":"Get swap rates","description":"Get informative swap rates from all available swappers. This does not create a transaction.","tags":["Swaps"],"parameters":[{"schema":{"type":"string","minLength":1,"example":"eip155:1/slip44:60"},"required":true,"name":"sellAssetId","in":"query"},{"schema":{"type":"string","minLength":1,"example":"eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"},"required":true,"name":"buyAssetId","in":"query"},{"schema":{"type":"string","pattern":"^\\d+$","description":"Exact amount of the sell asset to send, in base units. Required unless buyAmountCryptoBaseUnit is given.","example":"1000000000000000000"},"required":false,"name":"sellAmountCryptoBaseUnit","in":"query"},{"schema":{"type":"string","pattern":"^(?!0+$)\\d+$","description":"Exact amount of the buy asset to receive, in base units. Each rate returns the sell amount needed to get it. Mutually exclusive with sellAmountCryptoBaseUnit; swappers that cannot quote an exact output come back with an ExactOutputNotSupported error.","example":"100000"},"required":false,"name":"buyAmountCryptoBaseUnit","in":"query"},{"schema":{"type":"string","pattern":"^(?:\\d+)(?:\\.\\d+)?$","example":"0.01"},"required":false,"name":"slippageTolerancePercentageDecimal","in":"query"},{"schema":{"type":"string"},"required":false,"description":"Partner code for affiliate fee attribution. The API resolves the code to the registered affiliate address and BPS. Register a code at the affiliate dashboard.","name":"X-Partner-Code","in":"header","example":"vultisig"}],"responses":{"200":{"description":"Swap rates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateResponse"}}}},"400":{"description":"Invalid request"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"}}}},"/v1/swap/status":{"get":{"operationId":"getSwapStatus","summary":"Get swap status","description":"Look up the current status of a swap by its quote ID. Pass txHash on the first call after broadcasting to bind it to the quote and begin tracking; later calls can omit it. Externally paid quotes need no txHash - tracking starts from the quote ID alone and txHash is filled in once the provider reports the deposit - unless the application signed the deposit itself and passes its hash on the first call. A hash binds on the first call only.","tags":["Swaps"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"quoteId","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":128},"required":false,"name":"txHash","in":"query"},{"schema":{"type":"string"},"required":false,"description":"Partner code for affiliate fee attribution. The API resolves the code to the registered affiliate address and BPS. Register a code at the affiliate dashboard.","name":"X-Partner-Code","in":"header","example":"vultisig"}],"responses":{"200":{"description":"Swap status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwapStatusResponse"}}}},"400":{"description":"Invalid request parameters or txHash required to begin tracking"},"404":{"description":"Quote not found or expired"},"409":{"description":"Transaction hash mismatch"},"429":{"description":"Rate limit exceeded. Includes Retry-After header with seconds until reset.","headers":{"Retry-After":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}},"RateLimit-Limit":{"description":"Maximum requests allowed per window","schema":{"type":"integer","example":60}},"RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"integer","example":0}},"RateLimit-Reset":{"description":"Seconds until the rate limit window resets","schema":{"type":"integer","example":30}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal server error"},"503":{"description":"Swap service unavailable"},"504":{"description":"Swap service timed out"}}}}},"tags":[{"name":"Swap Widget SDK","description":"The `@shapeshiftoss/swap-widget` package is a drop-in React component that provides a complete swap interface — asset selection, rate comparison, wallet connection, transaction signing, and status tracking — backed by this API.\n\n> 📖 **The canonical, always-current reference is the package README:**\n> [`packages/swap-widget/README.md`](https://github.com/shapeshift/web/blob/develop/packages/swap-widget/README.md).\n> It documents every prop, the theming API, supported chains/swappers, and exported hooks. This page is a short orientation; defer to the README for details.\n\n## Installation\n\n```bash\nnpm install @shapeshiftoss/swap-widget\n```\n\nInstall the peer dependencies alongside it (React, wagmi/viem, React Query, and Reown AppKit):\n\n```bash\nnpm install react react-dom wagmi @wagmi/core viem \\\n  @tanstack/react-query \\\n  @reown/appkit @reown/appkit-adapter-wagmi \\\n  @reown/appkit-adapter-bitcoin @reown/appkit-adapter-solana \\\n  @solana/wallet-adapter-phantom @solana/wallet-adapter-solflare @solana/web3.js\n```\n\nImport the stylesheet once (required for the widget to render correctly):\n\n```tsx\nimport '@shapeshiftoss/swap-widget/style.css'\n```\n\n## Quick Start\n\n```tsx\nimport '@shapeshiftoss/swap-widget/style.css'\n\nimport { SwapWidget } from '@shapeshiftoss/swap-widget'\n\nfunction App() {\n  return (\n    <SwapWidget\n      walletConnectProjectId=\"your-walletconnect-project-id\"\n      partnerCode=\"your-partner-code\"\n      theme=\"dark\"\n      onSwapSuccess={txHash => console.log('Success:', txHash)}\n    />\n  )\n}\n```\n\n## Key things to know\n\n- **Wallet connection is built in.** The widget connects wallets via Reown AppKit (EVM, Bitcoin, and Solana).\n- **AppKit must be initialized before the widget mounts.** Either pass `walletConnectProjectId` and the widget initializes AppKit for you (get a free project ID at [dashboard.reown.com](https://dashboard.reown.com)), or call `createAppKit()` yourself in the host app (with a wagmi EVM adapter) before mounting the widget — the widget detects the shared singleton and supplies its own providers, so you wrap it in none. Pair the host-owned path with `showConnectButton={false}` to drive connection from your own UI. This requires `@reown/appkit*`/`wagmi`/`viem` to dedupe to a single shared copy. See the [README](https://github.com/shapeshift/web/blob/develop/packages/swap-widget/README.md#wallet-connection).\n- **`partnerCode` drives affiliate attribution.** It is forwarded to this API as the `X-Partner-Code` header. See the [Affiliate Program guide](https://github.com/shapeshift/web/blob/develop/docs/affiliates.md).\n- **Chain/asset filtering** uses the `sellFilters` and `buyFilters` props (objects with `allowedChainIds` / `disabledChainIds` / `allowedAssetIds` / `disabledAssetIds`). See the README for the full prop list and examples.\n\nFor the complete props reference, theming options, supported chains and swappers, and exported utilities/hooks, see the [package README](https://github.com/shapeshift/web/blob/develop/packages/swap-widget/README.md).\n"},{"name":"REST API Guide","description":"A step-by-step guide to executing a swap via the REST API. Full request/response schemas for every endpoint are in the reference sections below — this guide covers the flow and the semantics that aren't obvious from the schemas alone (quote expiry, status polling, errors).\n\nAll paths are relative to `https://api.shapeshift.com`. Send `X-Partner-Code: <your-code>` on the swap endpoints to attribute swaps for affiliate revenue (optional).\n\n## 1. Discover chains and assets\n\n```\nGET /v1/chains\nGET /v1/assets?chainId=eip155:1&limit=100&offset=0\n```\n\n`GET /v1/assets` supports optional `chainId`, `limit` (1–1000, default 100), and `offset` (default 0) query params for filtering and pagination. Use `GET /v1/assets/count` to size pagination. Look up a single asset with `GET /v1/assets/{assetId}` (the asset ID is a full CAIP-19 string).\n\n### Destination-only chains\n\nEach chain carries `isSellSupported`. Where it is `false` the chain can be **bought into but not sold from** — we have no way to give you a transaction to sign on it, so `/v1/swap/rates` and `/v1/swap/quote` reject it as a `sellAssetId` with a `400` and `code: 'UNSUPPORTED_SELL_CHAIN'`. It remains valid as a `buyAssetId`, where all you need is a receive address.\n\nThere is no per-asset equivalent — the constraint is a property of the chain, so filter your sell-asset list by joining assets to their `chainId`:\n\n```js\nconst sellable = new Set(chains.filter(c => c.isSellSupported).map(c => c.chainId))\nconst sellAssets = assets.filter(a => sellable.has(a.chainId))\n```\n\nThe set shrinks over time as more chains become executable, so read the flag rather than hardcoding the list.\n\n## 2. Get rates\n\n```\nGET /v1/swap/rates?sellAssetId=eip155:1/slip44:60&buyAssetId=eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48&sellAmountCryptoBaseUnit=1000000000000000000\nX-Partner-Code: your-partner-code\n```\n\nOptional `slippageTolerancePercentageDecimal` (e.g. `0.01` for 1%). The response returns a `rates` array (one entry per swapper, each with its own `swapperName`, amounts, fees, and an optional per-swapper `error`) plus `timestamp` and `expiresAt`. **Rates are indicative**, expire quickly (`expiresAt` ≈ 30s after issue), and are for display/comparison — request a quote to execute.\n\nEach rate also carries `supportsExternalPayment`. When true, the swapper can also take payment as a plain transfer to a deposit address, so any wallet can fund the swap and your application does not have to sign — though it still can, since the quote carries `transactionData` either way. See [Externally paid quotes](#externally-paid-quotes).\n\nA non-empty `allowanceContract` on a rate means executing that swapper pulls the sell token from an ERC-20 allowance. Clients that want to handle approvals themselves — checking the current allowance, or setting an unlimited approval ahead of time — can use it directly at this stage; otherwise the quote supplies ready-to-sign approval transactions.\n\n## 3. Get an executable quote\n\n```\nPOST /v1/swap/quote\nContent-Type: application/json\nX-Partner-Code: your-partner-code\n\n{\n  \"sellAssetId\": \"eip155:1/slip44:60\",\n  \"buyAssetId\": \"bip122:000000000019d6689c085ae165831e93/slip44:0\",\n  \"sellAmountCryptoBaseUnit\": \"1000000000000000000\",\n  \"swapperName\": \"Relay\",\n  \"receiveAddress\": \"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq\",\n  \"sendAddress\": \"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\",\n  \"slippageTolerancePercentageDecimal\": \"0.01\",\n  \"accountNumber\": 0\n}\n```\n\n- `swapperName` comes from the rate you chose in step 2.\n- `slippageTolerancePercentageDecimal` is optional; `accountNumber` is optional (defaults to `0`) and is needed for chains that derive addresses per account index (e.g. UTXO/Cosmos).\n- The response includes a `quoteId` (needed for status tracking), an `approval` object (whether an ERC-20 or TRC20 approval is required, the spender, and ready-to-sign `approvalTxs` when it is), and a `steps` array. Each step may include `transactionData` — a discriminated union on `type` (`evm`, `solana_instructions`, `solana_serialized_tx`, `utxo`, `cosmossdk_msg_send`, `cosmossdk_msg_deposit`, `tron`) — describing exactly what to sign for that chain.\n- Quotes expire. **Never sign, broadcast, or send a deposit after `expiresAt`** — request a fresh quote instead. `expiresAt` is the swapper's own deadline, not an arbitrary timeout: THORChain rotates its inbound addresses, externally paid swappers close their deposit channels, and funds sent to a closed channel can be lost. Swappers without a deadline of their own get a conservative 60s.\n\n## 4. Execute the swap\n\nThe API does **not** broadcast transactions — your application signs and broadcasts with the user's wallet:\n\n1. If `approval.isRequired` is true, sign and broadcast each transaction in `approval.approvalTxs` in order, waiting for each to confirm. These are **exact approvals** — sized to the step's `sellAmountCryptoBaseUnit` and consumed by the swap's execution, so a later swap needs its own approval unless a sufficient allowance is already in place (`approvalTxs` is empty in that case, with `isRequired: false`). Usually it is a single approve; tokens that require resetting a non-zero allowance before changing it (e.g. USDT) get a preceding `approve(spender, 0)`. Clients preferring an unlimited approval can build their own `approve(approval.spender, amount)` instead. Quotes are issued before approval exists — network fees are estimated as if the approval were already in place.\n2. For each step with `transactionData`, build, sign, and broadcast the transaction according to its `type` (EVM tx, Solana instructions, UTXO PSBT/deposit, Cosmos message, or Tron call/transfer — a `tron` step with `data` is a `TriggerSmartContract` to `to`, without it a transfer of the sell asset to `to`). A TRC20 sell can't be simulated until its approval lands, so such a step's network fee is a measured worst case; the transaction is unaffected since Tron charges actual energy under a fixed fee limit.\n3. Capture the resulting transaction hash for status tracking.\n\n### Externally paid quotes\n\nWhen a quote carries `depositAddress`, signing is optional. Send exactly `sellAmountCryptoBaseUnit` of the sell asset to that address before `expiresAt`, from any wallet — the payer does not have to be the user, and no wallet needs to be connected to your application. The quote still includes `transactionData` and `approval`, so a client with a connected wallet can execute it exactly as in step 4 instead; both paths land at the same address.\n\nPass the user's own address on the sell chain as `sendAddress` when requesting the quote. On the deposit path nothing is sent from it, but it is where a failed, expired or refunded swap returns funds, so it must be an address the user controls.\n\nA quote from a swapper advertising `supportsExternalPayment` may still omit `depositAddress` — treat that as the route needing a signed transaction after all, and fall back to the flow above.\n\n## 5. Track status\n\n```\nGET /v1/swap/status?quoteId=<quoteId>&txHash=0x...\n```\n\n- On the **first call after broadcasting**, include `txHash` to bind it to the quote and begin tracking. This sets status to `submitted`. Subsequent polls can omit `txHash`.\n- Externally paid quotes need no `txHash`: poll with `quoteId` alone from the moment the quote is issued, and `txHash` is filled in on the response once the deposit is detected. If your application signed the deposit itself, pass its `txHash` on the first call and it binds exactly as above. A hash binds on the first call only — one passed later is ignored.\n- `status` is one of `pending`, `submitted`, `confirmed`, `failed`. `pending` means tracking has begun but no sell transaction has been seen yet, which is an externally paid quote awaiting its deposit; `submitted` means one has. Poll until `confirmed` or `failed`; a `buyTxHash` appears once the destination transaction is known. `txLink` and `buyTxLink` are block explorer links for `txHash` and `buyTxHash`, and `swapperTxLink` is the provider's own page for the swap when it has one.\n- Poll at a modest interval (e.g. every 5–15s) and respect rate-limit headers. Stop polling on a terminal status.\n\n### Status errors\n\n- `404` `QUOTE_NOT_FOUND` — the quote is unknown, or aged out before tracking began (an hour past its deadline). Once tracking has begun, status stays available for as long as the swap is tracked. Request a new quote — unless a deposit was already sent, in which case that swap may still settle and a second quote would pay twice.\n- `400` `TX_HASH_REQUIRED` — no `txHash` was provided and none is bound yet; pass the broadcast tx hash. Externally paid quotes never return this.\n- `409` `TX_HASH_MISMATCH` — a different `txHash` is already bound to this quote.\n- `503` `SERVICE_UNAVAILABLE` — tracking could not start or be read. Repeat the same call, including `txHash`.\n\n## Affiliate reporting (optional)\n\nOnce live, partners can review attributed activity by partner code:\n\n```\nGET /v1/affiliate/stats?partnerCode=your-partner-code\nGET /v1/affiliate/swaps?partnerCode=your-partner-code\n```\n\nYou can also resolve a code to its attribution details (partner address and bps split) with `GET /v1/partner/{code}`.\n"}]}