# Allways Access API v1 ## About and links Allways Access is the Ventura Labs web app and API for Allways (Bittensor Subnet 7), a marketplace for moving value between native assets. Exchange rates are posted up front, the native asset arrives in the wallet you choose, and every transaction is backed by locked funds worth its full value. Web app: https://allways.venturalabs.ai Exchange pairs with live exchange rates: https://allways.venturalabs.ai/exchange Allways protocol: https://all-ways.io Allways docs: https://docs.all-ways.io Allways source: https://github.com/entrius/allways This is the mainnet API. Real funds on every network. Swap assets in five calls. Base URL: https://api-allways.venturalabs.ai/v1 Auth: x-api-key on /exchanges* and /account*. /rate, /rates and /currencies are open. Get a key on the web app at https://allways.venturalabs.ai/api-access (sign in first). Keys start alw_live_ and regeneration invalidates the old key. Headless key (no browser; these calls sit outside /v1): POST https://api-allways.venturalabs.ai/auth/wallet/challenge {address} -> {challenge,nonce}; sign the raw challenge bytes with the wallet's sr25519 key; POST https://api-allways.venturalabs.ai/auth/wallet/verify {address,nonce,signature:"0x..",termsAccepted:true} -> {token}; POST https://api-allways.venturalabs.ai/account/api-key with Authorization: Bearer -> {apiKey,createdAt}, shown once. A key cannot mint or rotate keys; only a session can. Amounts are decimal strings in whole currency units. toAmount is net of fee and final at creation. GET /currencies -> [{id,symbol,network,decimals,chainId?,pairsWith[]}]; chainId is set for EVM assets GET /rate?from=sol&to=tao&amount=0.5 -> {from,to,fromAmount,toAmount,rate,minFromAmount,maxFromAmount,levels[]} GET /rates?to=tao&toAmount=0.1[&from=sol,btc] -> [{from,quote:rate|null,error:{code,message,...}|null}]; every currency that pays into to when from is omitted POST /exchanges body {from,to,fromAmount,fromAddress,toAddress,slippage?,minToAmount?} -> exchange (201) GET /exchanges/{id} -> exchange GET /exchanges -> last 100, newest first; ?updatedSince= -> every exchange updated at or after it, oldest update first, no cap POST /exchanges/{id}/sent body {txHash} -> exchange; optional, safe right after broadcast, speeds up detection. Subnet token sent with btcli's MEV shield: post what btcli prints (-) or the inner transfer hash; the shield's own hash is not a deposit GET /account -> {accountId,displayName,apiKeyCreatedAt,balance,pending,depositAddress,reservationCost,creditAsset,taoPerCredit,minTopUpTao}; accountId and displayName say which account the key belongs to, apiKeyCreatedAt when that key was minted DELETE /account/api-key -> 204; revokes the key sent with the request, at once ## Examples (curl; $API is the origin, $KEY your key) curl "$API/v1/currencies" curl "$API/v1/rate?from=sol&to=tao&amount=0.5" -> {"from":"sol","to":"tao","fromAmount":"0.5","toAmount":"3.912","rate":"7.824","minFromAmount":"0.1","maxFromAmount":"40","levels":[{"rate":"7.824","maxFromAmount":"12.4"}]} curl -X POST "$API/v1/exchanges" -H "x-api-key: $KEY" -H "content-type: application/json" \ -d '{"from":"sol","to":"tao","fromAmount":"0.5","fromAddress":"9s4hVRU5nfPF9Er9Y5FexDjxNtvZSYmnVEujxHCX5Gsb","toAddress":"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"}' -> 201 {"id":"5d1f9c2e-8b7a-4c3d-9e2f-1a2b3c4d5e6f","status":"pending","toAmount":"3.912","depositAddress":null,"validUntil":null,...} curl "$API/v1/exchanges/5d1f9c2e-8b7a-4c3d-9e2f-1a2b3c4d5e6f" -H "x-api-key: $KEY" -> {"status":"awaiting_deposit","depositAddress":"7Gh2kLxQ9Yb1VfP3nR8sT4uW6xZ2aB5cD7eF9gH1jK3m","validUntil":1756900612,...} curl -X POST "$API/v1/exchanges/5d1f9c2e-8b7a-4c3d-9e2f-1a2b3c4d5e6f/sent" -H "x-api-key: $KEY" -H "content-type: application/json" -d '{"txHash":""}' curl "$API/v1/account" -H "x-api-key: $KEY" -> {"accountId":"9f1c2e4a-7b3d-4c58-9e21-0a1b2c3d4e5f","displayName":"swift-otter-4821","apiKeyCreatedAt":1756900000,"balance":3,"pending":1,"depositAddress":"5HqUkGuo62b5ELGrqfUbVWFxUhMmGmYT4tcRvAX1p5RvUxrA","reservationCost":1,"creditAsset":"alpha token","taoPerCredit":"0.0066","minTopUpTao":"0.03"} curl -X DELETE "$API/v1/account/api-key" -H "x-api-key: $KEY" -> 204 Exchange: {id,status,from,to,fromAmount,toAmount,rate,depositAddress,validUntil,fromAddress,toAddress,fromTxHash,toTxHash,refundAsset,refundAddress,reason,createdAt,updatedAt,reference} depositAddress and validUntil are null until awaiting_deposit; validUntil is usually about 5 minutes ahead. Send exactly fromAmount from fromAddress; the network fee is on top. fromAddress must be copied from the wallet tool, never retyped (case-sensitive); a deposit from any other address is unrecoverable. minFromAmount and maxFromAmount are inclusive. from=sn: depositAddress is a coldkey. Send ONE transfer_stake, not a transfer: origin netuid = dest netuid = N, exactly fromAmount, from one hotkey that holds all of it, signed by the fromAddress coldkey. No batch, no proxy, no --all. btcli stake transfer --wallet-name --hotkey --origin-netuid N --dest-netuid N --dest --amount Poll every 3 seconds. Normal order: pending -> awaiting_deposit -> confirming -> exchanging -> finished. pending ends as awaiting_deposit or failed (pool_lost | reserve_rejected, credit refunded) within 15 minutes. Stop on finished, refunded, or failed. Statuses: pending | awaiting_deposit | confirming | exchanging | finished | refunded | failed Failed reasons: pool_lost | reserve_rejected | claim_stale | dest_unpayable | asset_unavailable | deposit_lapsed Errors are {code,message,...extra}. 400 validation {field} | invalid_pair | invalid_amount | tx_not_found | wrong_reservation 401 unauthorized 402 insufficient_balance {balance,cost,depositAddress} 403 geo_blocked | sanctioned 404 not_found 409 seat_taken {retryAfter} | rate_below_floor {toAmount,floor} | not_awaiting_deposit 422 amount_too_low | amount_too_high | no_liquidity {minFromAmount,maxFromAmount} | reserve_rejected {reason} 429 rate_limited {retryAfter}; Retry-After header 500 internal_error; retry after 30 s, give up and report after 10 minutes Each exchange costs 1 credit; balance is fractional and must be >= reservationCost to create. Eligible new accounts receive 2 welcome credits. Credits refund on refunded, and on failed with pool_lost, reserve_rejected, claim_stale, dest_unpayable, or asset_unavailable. deposit_lapsed keeps the credit (refunded only if the exchange expired before the deposit window ended). dest_unpayable: the funds you sent are not returned; only the credit is refunded. Make sure toAddress can receive the asset before you create the exchange. asset_unavailable: the subnet token in to was shut down; the funds you sent are not returned; only the credit is refunded. Do not retry the same to. Top up by sending TAO to GET /account depositAddress: credits wanted x taoPerCredit, at least minTopUpTao. Balance rises within about 2 minutes. ## Widgets (page embeds; one publishable alw_pub_ key for both, safe in page source, created at https://allways.venturalabs.ai/widget-access or https://allways.venturalabs.ai/checkout-dashboard) Exchange widget: a complete swap in one iframe. Free while in preview: no credit is spent. It sends no events to the page; its swaps are in GET /exchanges. Docs: https://api-allways.venturalabs.ai/v1/docs/widget Checkout widget: a button opens a payment modal. The customer pays with the asset they hold; the vendor receives one asset in one wallet. Docs: https://api-allways.venturalabs.ai/v1/docs/checkout (raw: https://api-allways.venturalabs.ai/v1/docs/checkout.md) Set at https://allways.venturalabs.ai/checkout-dashboard, never in the page: receive asset and wallet, amounts, account mode, webhook URL. The signing secret is on its Developers tab; keep it on the server. then AllwaysCheckout.open({key,account?,theme?,onSuccess?,onClose?}); AllwaysCheckout.close(). Load checkout.js from https://allways.venturalabs.ai; never copy, bundle or proxy it. key: required. account: the customer's ID in the vendor's system, 1 to 128 characters of A-Z a-z 0-9 . _ : @ + -. theme: auto (default) | light | dark. onClose(): the modal closed, paid or not. onSuccess({id,account}): once, when the payment is delivered and the modal is open. Display only, never a credit signal: a customer can call it from the browser console, and it does not fire when the modal closes first. Account modes (dashboard setting -> account in the webhook): site (default; the page must pass account) | ask (the customer types an email or username) | wallet (the paying address) | none (null). A passed account wins in every mode. Webhook: one JSON POST to the vendor's https URL when a payment ends: {event,id,account,asset,amount,settled,txHash,test} event follows the money: payment.delivered (in the vendor wallet; amount = what the customer bought, in asset) | payment.refunded (back with the customer; amount "0") | payment.failed (the customer paid, nothing could be delivered; amount "0"). A payment the customer never sent has no event. settled: {asset, amount} = what landed in the vendor wallet, null unless delivered. Credit amount. settled differs only when collateral paid in place of a missed delivery (about 110%, in SOL or TAO). Header x-allways-signature: t=,v1=. v1 = HMAC-SHA256 of "." with the signing secret, over the raw bytes before JSON parsing; compare in constant time. Reject a t more than 5 minutes from your clock. Answer 2xx within 5 seconds; anything else is retried after 1 min, 5 min, 30 min, 2 h, 6 h, 12 h. The same id can arrive again on a retry or a resend. Credit only on payment.delivered, from amount, once per id. Ignore test:true in production (a dashboard test payment: id test_..., no money moves). Direct payment: a vendor that receives TAO or a subnet token can also be paid in that same asset only (TAO for TAO, snN for snN; first in the modal), straight to its wallet. No exchange, no custody. The modal asks for an exact amount = amount picked + a random tail under 0.001. Only a transfer of exactly that amount, within 24 hours, is matched; the sender does not matter. Same webhook; amount = settled = what was sent (e.g. "5.000137"), so a fixed-price shop checks amount >= price, never equality. A transfer within 1% of the exact amount, that can belong to one payment only, waits in the vendor dashboard as Needs review; payment.delivered is sent (amount = what arrived) once the vendor accepts it, or at once when the vendor turned on automatic acceptance in Settings. Any other amount, or another asset (e.g. alpha sent to a TAO shop), lands in the wallet with no event and cannot be returned by us. Poll fallback, with or without a webhook: GET /exchanges?updatedSince= (x-api-key). The cursor is inclusive, so the last exchange of one poll returns in the next: credit each id once. reference = account, to = asset, toAmount = amount when status is finished; status refunded = payment.refunded, except a refunded exchange whose refundAddress is the vendor wallet = payment.delivered. Test payments are never listed. Each payment costs the vendor 1 credit, taken when the customer starts it; returned on refunded or failed, kept when the customer never sends. Agent procedure: https://api-allways.venturalabs.ai/skill.txt Docs: https://api-allways.venturalabs.ai/v1/docs Schema: https://api-allways.venturalabs.ai/v1/openapi.json