{"openapi":"3.0.0","paths":{"/v1/docs":{"get":{"operationId":"DocsController_root","parameters":[],"responses":{"200":{"description":""}}}},"/v1/docs/index.json":{"get":{"operationId":"DocsController_index","parameters":[],"responses":{"200":{"description":""}}}},"/v1/docs/{slug}.md":{"get":{"operationId":"DocsController_raw","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}}}},"/v1/docs/{slug}":{"get":{"operationId":"DocsController_html","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}}}},"/v1/llms.txt":{"get":{"operationId":"DocsController_agents","parameters":[],"responses":{"200":{"description":""}}}},"/v1/skill.txt":{"get":{"operationId":"DocsController_skill","parameters":[],"responses":{"200":{"description":""}}}},"/llms.txt":{"get":{"operationId":"AgentFilesController_agents","parameters":[],"responses":{"200":{"description":""}}}},"/skill.txt":{"get":{"operationId":"AgentFilesController_skill","parameters":[],"responses":{"200":{"description":""}}}},"/v1/currencies":{"get":{"operationId":"CurrenciesController_list","summary":"List currencies","description":"Public, no key. id is the value for from and to on every other call. Several assets share a symbol on different networks, so always pick by id.","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CurrencyDto"}}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["currencies"]}},"/v1/rate":{"get":{"operationId":"RateController_best","summary":"Quote an amount","description":"Public, no key. Not a lock: the rate is fixed when you create the exchange. toAmount and rate price your whole amount, net of fee: toAmount is the number you get. levels is informational: each rung is a rate available right now and the largest single order it can fill; you are always given the best rung that fits your amount. Never combine rungs.","parameters":[{"name":"amount","required":false,"in":"query","description":"Decimal string in from units, not smallest units","schema":{"example":"0.5","type":"string"}},{"name":"toAmount","required":false,"in":"query","description":"Desired net amount in to units; use instead of amount","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","description":"Currency id to send — an id from /v1/currencies","schema":{"example":"sol","type":"string"}},{"name":"to","required":true,"in":"query","description":"Currency id to receive — an id from /v1/currencies","schema":{"example":"tao","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateDto"}}}},"400":{"description":"invalid_pair · invalid_amount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"422":{"description":"amount_too_low · amount_too_high · no_liquidity — the body carries minFromAmount and maxFromAmount","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["rate"]}},"/v1/rates":{"get":{"operationId":"RatesController_all","summary":"Quote one received amount in every currency","description":"Public, no key. What each currency costs to receive toAmount of to, in one call. Each entry carries a quote, or the error GET /v1/rate would return for that pair. Not a lock.","parameters":[{"name":"from","required":false,"in":"query","description":"Comma-separated currency ids to price; omit for every currency that pays into to","schema":{"example":"sol,btc","type":"string"}},{"name":"to","required":true,"in":"query","description":"Currency id to receive — an id from /v1/currencies","schema":{"example":"tao","type":"string"}},{"name":"toAmount","required":true,"in":"query","description":"Desired net amount in to units","schema":{"example":"0.1","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RatesEntryDto"}}}}},"400":{"description":"invalid_pair · invalid_amount · validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["rate"]}},"/v1/exchanges":{"post":{"operationId":"ExchangesController_create","summary":"Create an exchange","description":"Costs reservationCost credits (see /v1/account), spent at creation. toAmount in the response is final. depositAddress and validUntil are null in the 201 and fill when status becomes awaiting_deposit — poll GET /v1/exchanges/{id} to get them.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExchangeDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeDto"}}}},"400":{"description":"validation — the body names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"402":{"description":"insufficient_balance — the body carries balance, cost and depositAddress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked · sanctioned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"409":{"description":"seat_taken (retryAfter) · rate_below_floor (toAmount, floor)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"422":{"description":"amount_too_low · amount_too_high · no_liquidity · reserve_rejected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["exchanges"],"security":[{"apiKey":[]}]},"get":{"operationId":"ExchangesController_list","summary":"List your exchanges","description":"The last 100 exchanges of your account, newest first.","parameters":[{"name":"updatedSince","required":false,"in":"query","description":"Return every exchange updated at or after this unix timestamp","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ExchangeDto"}}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["exchanges"],"security":[{"apiKey":[]}]}},"/v1/exchanges/{id}":{"get":{"operationId":"ExchangesController_get","summary":"Read an exchange","description":"Poll every 3 seconds. Stop on finished, refunded or failed. The flow is pending → awaiting_deposit → confirming → exchanging → finished; refunded and failed are the other terminal outcomes.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeDto"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["exchanges"],"security":[{"apiKey":[]}]}},"/v1/exchanges/{id}/sent":{"post":{"operationId":"ExchangesController_sent","summary":"Report your deposit","description":"Optional. Call it while awaiting_deposit, right after you send, with the transaction hash. Skipping it only delays the claim until our scanner sees the deposit.","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExchangeDto"}}}},"400":{"description":"validation · tx_not_found · wrong_reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"409":{"description":"not_awaiting_deposit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["exchanges"],"security":[{"apiKey":[]}]}},"/v1/account":{"get":{"operationId":"AccountController_get","summary":"Read your account","description":"Which account the key belongs to, your credit balance, the credits held by in-flight exchanges, and the address to top up.","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountDto"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["account"],"security":[{"apiKey":[]}]}},"/v1/account/api-key":{"delete":{"operationId":"AccountController_revokeKey","summary":"Revoke your API key","description":"Deletes the key sent with this request. It stops working at once; mint a new one from a session.","parameters":[{"name":"x-api-key","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"204":{"description":"revoked"},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"403":{"description":"geo_blocked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}},"429":{"description":"rate_limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorDto"}}}}},"tags":["account"],"security":[{"apiKey":[]}]}}},"info":{"title":"Allways Access API","description":"Swap one asset for another in five steps:\n1. GET /v1/rate?from=sol&to=tao&amount=0.5 — quote (public; not a lock).\n2. POST /v1/exchanges — create; costs reservationCost credits (GET /v1/account); toAmount is final from here.\n3. Poll GET /v1/exchanges/{id} until status is awaiting_deposit — depositAddress and validUntil are null before that.\n4. Send exactly fromAmount to depositAddress before validUntil (unix seconds). Optionally POST /v1/exchanges/{id}/sent with the tx hash. A subnet token sent with btcli's MEV shield: post what btcli prints (<block>-<idx>) or the inner transfer hash; the shield's own hash is not a deposit.\n5. Poll GET /v1/exchanges/{id} every 3 seconds until finished, refunded or failed.\n\nAuth: header x-api-key on every route except /v1/currencies, /v1/rate and /v1/rates. Get a key at /api-access on the web app, or headless: POST /auth/wallet/challenge {address}, sign the raw challenge with the wallet's sr25519 key, POST /auth/wallet/verify {address,nonce,signature,termsAccepted:true} for a bearer token, then POST /account/api-key with it. DELETE /v1/account/api-key revokes the key; only a session mints or rotates one. Top up credits at the depositAddress from GET /v1/account.\nCurrency ids are lowercase (sol, tao) and come from GET /v1/currencies — several assets share a symbol on different networks, so pick by id.\nAmounts are decimal strings in the currency's own units (\"0.5\"), never smallest units. toAmount is what lands in your wallet, net of fee.\nErrors: every non-2xx body is { code, message, ...detail }; codes are the closed enum on ApiErrorDto. 429 and 409 seat_taken carry Retry-After.","version":"1.0.0","contact":{}},"tags":[],"servers":[{"url":"https://api-allways.venturalabs.ai"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"}},"schemas":{"CurrencyDto":{"type":"object","properties":{"id":{"type":"string","description":"Use as from / to","example":"sol"},"symbol":{"type":"string","example":"SOL"},"network":{"type":"string","example":"Solana"},"decimals":{"type":"number","example":9},"chainId":{"type":"number","description":"EVM assets only: the chain id of the network they settle on","example":1},"contract":{"type":"string","description":"Tokens only: the token contract (EVM) or mint (Solana). Absent for a native coin","example":"0xaf88d065e77c8cC2239327C5EDb3A432268e5831"},"pairsWith":{"example":["tao","btc","eth"],"type":"array","items":{"type":"string"}}},"required":["id","symbol","network","decimals","pairsWith"]},"ApiErrorDto":{"type":"object","properties":{"code":{"type":"string","enum":["validation","invalid_pair","invalid_amount","amount_too_low","amount_too_high","no_liquidity","unauthorized","not_found","insufficient_balance","geo_blocked","sanctioned","seat_taken","rate_below_floor","reserve_rejected","not_awaiting_deposit","tx_not_found","wrong_reservation","rate_limited","internal_error","invalid_widget_key","captcha_failed","widget_unavailable","price_changed","checkout_unavailable","network_paused"],"example":"seat_taken"},"message":{"type":"string","description":"Human copy, may change"},"field":{"type":"string","description":"validation: the body field at fault"},"minFromAmount":{"type":"string","description":"amount_too_low / amount_too_high / no_liquidity: decimal in from units"},"maxFromAmount":{"type":"string","description":"amount_too_low / amount_too_high / no_liquidity: decimal in from units"},"retryAfter":{"type":"number","description":"seat_taken / rate_limited: seconds to wait, also sent as Retry-After"},"balance":{"type":"number","description":"insufficient_balance: your credits"},"cost":{"type":"number","description":"insufficient_balance: credits one exchange costs"},"depositAddress":{"type":"object","description":"insufficient_balance: where to send the credit asset","nullable":true},"toAmount":{"type":"string","description":"rate_below_floor: best net amount available right now"},"floor":{"type":"string","description":"rate_below_floor: the minToAmount you asked for"},"reason":{"type":"string","description":"reserve_rejected: why, in plain words"}},"required":["code","message"]},"RateLevelDto":{"type":"object","properties":{"rate":{"type":"string","description":"to per 1 from, net of fee","example":"7.824"},"maxFromAmount":{"type":"string","description":"Largest amount at this rate","example":"12.4"}},"required":["rate","maxFromAmount"]},"RateDto":{"type":"object","properties":{"from":{"type":"string","example":"sol"},"to":{"type":"string","example":"tao"},"fromAmount":{"type":"string","example":"0.5"},"toAmount":{"type":"string","description":"Net of fee","example":"3.912"},"rate":{"type":"string","description":"toAmount per 1 fromAmount","example":"7.824"},"minFromAmount":{"type":"string","example":"0.1"},"maxFromAmount":{"type":"string","example":"12.4"},"levels":{"type":"array","items":{"$ref":"#/components/schemas/RateLevelDto"}}},"required":["from","to","fromAmount","toAmount","rate","minFromAmount","maxFromAmount","levels"]},"RatesEntryDto":{"type":"object","properties":{"from":{"type":"string","example":"sol"},"quote":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/RateDto"}]},"error":{"nullable":true,"allOf":[{"$ref":"#/components/schemas/ApiErrorDto"}]}},"required":["from","quote","error"]},"CreateExchangeDto":{"type":"object","properties":{"from":{"type":"string","description":"Currency id to send","example":"sol"},"to":{"type":"string","description":"Currency id to receive","example":"tao"},"fromAmount":{"type":"string","description":"Decimal string in from units, e.g. \"0.5\" — not smallest units","example":"0.5"},"fromAddress":{"type":"string","description":"Your address on the from currency; refunds land here","example":"So1…"},"toAddress":{"type":"string","description":"Your address on the to currency","example":"5F…"},"slippage":{"type":"number","description":"Maximum rate movement in percent; defaults to 0.5","example":0.5},"minToAmount":{"type":"string","description":"Minimum net amount to receive; overrides slippage","example":"3.90"}},"required":["from","to","fromAmount","fromAddress","toAddress"]},"ExchangeDto":{"type":"object","properties":{"id":{"type":"string","example":"5d1f3c2e-8a1b-4c7d-9e0f-123456789abc"},"status":{"type":"string","enum":["pending","awaiting_deposit","confirming","exchanging","finished","refunded","failed"],"example":"pending","description":"Normal order: pending → awaiting_deposit → confirming → exchanging → finished. Terminal: finished, refunded, failed."},"from":{"type":"string","example":"sol"},"to":{"type":"string","example":"tao"},"fromAmount":{"type":"string","description":"Amount to send","example":"0.5"},"toAmount":{"type":"string","description":"Amount that lands, net of fee","example":"3.912"},"rate":{"type":"string","description":"toAmount per 1 fromAmount","example":"7.824"},"depositAddress":{"type":"string","description":"Send exactly fromAmount here; null until awaiting_deposit","nullable":true},"validUntil":{"type":"number","description":"Send-by deadline, unix seconds; null until awaiting_deposit","nullable":true},"fromAddress":{"type":"string","example":"So1…","nullable":true},"toAddress":{"type":"string","example":"5F…","nullable":true},"fromTxHash":{"type":"string","nullable":true},"toTxHash":{"type":"string","nullable":true},"refundAsset":{"type":"string","description":"Set on refunded","nullable":true},"refundAddress":{"type":"string","description":"Set on refunded","nullable":true},"reason":{"type":"string","enum":["pool_lost","reserve_rejected","claim_stale","dest_unpayable","asset_unavailable","deposit_lapsed"],"nullable":true,"description":"Set on failed. pool_lost, reserve_rejected, claim_stale, dest_unpayable and asset_unavailable refund the credit; deposit_lapsed (no funds sent) keeps it. claim_stale: your deposit moved but the claim went stale — contact support. dest_unpayable: toAddress could not receive funds. asset_unavailable: the subnet token was shut down."},"createdAt":{"type":"number","example":1756900000},"updatedAt":{"type":"number","example":1756900000},"reference":{"type":"string","nullable":true}},"required":["id","status","from","to","fromAmount","toAmount","rate","depositAddress","validUntil","fromAddress","toAddress","fromTxHash","toTxHash","refundAsset","refundAddress","reason","createdAt","updatedAt","reference"]},"SentDto":{"type":"object","properties":{"txHash":{"type":"string","description":"Hash of your deposit transaction. For a subnet token sent with btcli's MEV shield, the extrinsic id btcli prints (<block>-<idx>) or the inner transfer hash; the shield's own hash is not a deposit.","example":"3nB…"}},"required":["txHash"]},"AccountDto":{"type":"object","properties":{"accountId":{"type":"string","description":"Which account this key belongs to; quote it in support requests","example":"9f1c2e4a-7b3d-4c58-9e21-0a1b2c3d4e5f"},"displayName":{"type":"string","description":"The account's name, as the app shows it","example":"swift-otter-4821"},"apiKeyCreatedAt":{"type":"number","description":"When the key sent with this request was minted, unix seconds; regenerating invalidates the old key, so this says which one you hold","nullable":true,"example":1756900000},"balance":{"type":"number","description":"Credits available","example":3},"pending":{"type":"number","description":"Credits held by pending exchanges","example":1},"depositAddress":{"type":"string","description":"Send the credit asset here to top up","nullable":true},"reservationCost":{"type":"number","description":"Credits per exchange","example":1},"creditAsset":{"type":"string","example":"alpha token"},"taoPerCredit":{"type":"string","description":"TAO that buys one credit right now; null while the price is unknown","nullable":true,"example":"0.0066"},"minTopUpTao":{"type":"string","description":"Smallest deposit that is credited, in TAO","example":"0.03"}},"required":["accountId","displayName","apiKeyCreatedAt","balance","pending","depositAddress","reservationCost","creditAsset","taoPerCredit","minTopUpTao"]}}}}