# Allways Access — agent skill This is the mainnet API. Real funds on every network. Swap one crypto asset for another through the Allways Access API. Native assets, no bridge, no custody. Five HTTP calls and polling. This file is the procedure; the reference is https://api-allways.venturalabs.ai/v1/llms.txt and the schema is https://api-allways.venturalabs.ai/v1/openapi.json. ## When to use - A task needs asset X turned into asset Y on-chain, and you can send X from a wallet. - One side of every pair is SOL, TAO, or a subnet token (sn); two subnet tokens never pair. GET https://api-allways.venturalabs.ai/v1/currencies lists ids and pairsWith. - A web page needs a swap box or a crypto payment button: see Widgets below. The page makes no API call. ## What you need from the human (once) 1. An API key. The human signs in at https://allways.venturalabs.ai, opens https://allways.venturalabs.ai/api-access, presses Generate key, and hands you the key. It starts with alw_live_ and is shown once. Send it as x-api-key. Or mint it yourself with a Bittensor wallet the human gives you (sr25519 keypair): POST https://api-allways.venturalabs.ai/auth/wallet/challenge {"address"} -> {challenge, nonce}; sign the raw challenge bytes; 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}. The nonce is good for 5 minutes, once. 2. Credits. One exchange costs one credit. Google or GitHub sign-ins start with 2 free credits. 3. A source wallet that can send X, and a destination address that can receive Y. ## Fill up your credits Call GET https://api-allways.venturalabs.ai/v1/account with the key. Read accountId and displayName first and tell the human which account the key belongs to: several keys on several accounts are easy to mix up, and the credits you spend are that account's. Then read depositAddress, taoPerCredit, and minTopUpTao. Ask the human to send TAO on Bittensor to depositAddress: credits wanted x taoPerCredit, and at least minTopUpTao (smaller deposits are not credited). Balances are fractional; a create needs balance >= reservationCost. No API write. Poll GET /v1/account every 30 seconds; balance rises within about 2 minutes. A create that returns 402 insufficient_balance carries the same depositAddress. ## Procedure 1. GET https://api-allways.venturalabs.ai/v1/rate?from=sol&to=tao&amount=0.5 (no key). Read toAmount, minFromAmount, maxFromAmount. Keep fromAmount inside the bounds; both ends are inclusive. 2. POST https://api-allways.venturalabs.ai/v1/exchanges with x-api-key and body {"from","to","fromAmount","fromAddress","toAddress"}. 201 returns id and a final toAmount. One credit is spent here. 3. Poll GET https://api-allways.venturalabs.ai/v1/exchanges/{id} every 3 seconds until status is awaiting_deposit (usually a few seconds, at most about 30). If it stays pending, keep polling: within 15 minutes it becomes awaiting_deposit or failed with pool_lost or reserve_rejected (credit refunded). Read depositAddress and validUntil (unix seconds, usually about 5 minutes ahead). 4. Send exactly fromAmount from fromAddress to depositAddress before validUntil. The network fee is on top; the amount that lands must equal fromAmount. Optional: POST https://api-allways.venturalabs.ai/v1/exchanges/{id}/sent {"txHash"} right after broadcast, before confirmation; it speeds up detection and returns the exchange, often already confirming. A 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. Solana CLI example: solana transfer --from --fee-payer --allow-unfunded-recipient -u ; the printed Signature is the txHash. Subnet token (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 SDK: subtensor.transfer_stake(wallet=wallet, destination_coldkey_ss58=, hotkey_ss58=, origin_netuid=N, destination_netuid=N, amount=bt.Balance.from_tao(, netuid=N)). 5. Poll every 3 seconds until status is finished, refunded, or failed. On finished, report toTxHash. Never stop before a terminal status. ## When you are done DELETE https://api-allways.venturalabs.ai/v1/account/api-key with the key revokes it at once (204). Do it when the task is over, or the moment the key may have leaked. A key cannot mint a new one; that needs a fresh sign-in. ## Rules - Do not send before awaiting_deposit. Do not send after validUntil. - fromAddress must be the exact address of the wallet that will send. Never retype it: read it from the wallet tool (for Solana: solana address -k ) and paste the output. Addresses are case-sensitive. A deposit from any other address is never matched to the exchange and cannot be recovered; it is gone. - Before sending, read fromAddress back from GET /v1/exchanges/{id} and compare it byte for byte with the wallet tool's address. If they differ, do not send: let the exchange lapse (one credit, no funds lost). - 400 validation saying an address is a few characters away from one you used before: you mistyped it. Copy it from the wallet again; never edit the suggested value by hand. - toAmount at 201 is what lands. If it is too low, do not send; the exchange lapses. - 409 seat_taken: wait retryAfter seconds, re-quote, create again. - 422 amount_too_low, amount_too_high, no_liquidity: change fromAmount to fit the bounds. - failed with reason pool_lost, reserve_rejected, claim_stale, dest_unpayable, or asset_unavailable: the credit is refunded; re-quote and retry. dest_unpayable means toAddress cannot receive Y — fix the address first. asset_unavailable means the subnet token Y was shut down — do not retry the same to. Funds already sent are not returned. - failed with reason deposit_lapsed: you did not send in time; the credit is kept. - refunded: the other side did not deliver; you receive 110% of the value in refundAsset (SOL or TAO, priced when the rate was locked) at refundAddress, and the credit is returned. A subnet-token leg is paid in TAO to that leg's Bittensor address. - 429 rate_limited: wait Retry-After seconds. - 500 internal_error: retry after 30 seconds. If it persists for 10 minutes the service is down: stop, spend nothing, and report it. ## Widgets Both embeds use one publishable key, alw_pub_..., safe in page source. The human creates it at https://allways.venturalabs.ai/widget-access or https://allways.venturalabs.ai/checkout-dashboard and hands it to you. Exchange widget: a complete swap in one iframe. Free while in preview; no credit is spent. theme is auto, light, or dark. The widget sends no events to the page; its swaps are in GET /v1/exchanges. Page: 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 human receives one asset in one wallet. One credit per payment. 1. The human sets up https://allways.venturalabs.ai/checkout-dashboard: receive asset and wallet, amounts, account mode, webhook URL. The page never sets these. The signing secret is on the Developers tab: read it from an environment variable, never commit it. 2. Add to the page, loaded from https://allways.venturalabs.ai (do not copy, bundle or proxy checkout.js): AllwaysCheckout.open({ key, account, theme, onSuccess, onClose }) key: the alw_pub_ key. theme: auto (default), light, or dark. account: the customer's ID in the human's system, rendered by the server: 1 to 128 characters of A-Z a-z 0-9 . _ : @ + - onSuccess({id, account}) fires once when the payment is delivered and the modal is open. onClose() fires when the modal closes, paid or not. 3. The account mode (a dashboard setting) decides the account the webhook carries. 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. 4. Add an https POST route for the webhook. One JSON body arrives when a payment ends: {event, id, account, asset, amount, settled, txHash, test}. event follows the money: payment.delivered (in the vendor wallet; amount is what the customer bought, in asset), payment.refunded (back with the customer; amount is "0") or payment.failed (the customer paid and nothing could be delivered; amount is "0"). settled is {asset, amount}: what landed in the wallet. It differs from amount only when collateral paid in place of a missed delivery. 5. Verify before any other step. Header x-allways-signature: t=,v1=. v1 is the HMAC-SHA256 of "." with the signing secret, in hex, over the raw bytes before JSON parsing. Compare in constant time. Reject a t more than 5 minutes from your clock. 6. Credit only on payment.delivered: add amount to account, once per id. The same id can arrive again on a retry or a resend. Ignore "test": true in production. Answer 2xx within 5 seconds; anything else is retried 6 times over about 20 hours. When the vendor receives TAO or a subnet token, the customer can also pay in that same asset only (TAO for TAO, snN for snN), straight to the vendor wallet. The modal asks for an exact amount (the amount picked plus a small random tail) and amount in the webhook is that full figure, e.g. "5.000137". For a fixed price, check amount >= price, never equality. An amount off by up to 1% waits in the vendor dashboard (Needs review) until the vendor accepts it, or is accepted at once when the vendor turned that on in Settings. 7. Poll as a fallback, with or without a webhook: GET https://api-allways.venturalabs.ai/v1/exchanges?updatedSince= with x-api-key returns every exchange updated at or after the cursor. The cursor is inclusive, so the last exchange of one poll returns in the next: credit each id once. reference is the account, to is the asset, toAmount is the amount when status is finished. Never credit from onSuccess. It is display only: a customer can call it from the browser console, and it does not fire when the modal closes first. Page, with verify samples in Node and Python: https://api-allways.venturalabs.ai/v1/docs/checkout (raw: add .md) ## Endpoints GET /v1/currencies open GET /v1/rate?from&to&amount open GET /v1/rates?to&toAmount[&from] open, every currency at once POST /v1/exchanges x-api-key GET /v1/exchanges/{id} x-api-key GET /v1/exchanges x-api-key, last 100; ?updatedSince= for checkout POST /v1/exchanges/{id}/sent x-api-key, optional GET /v1/account x-api-key DELETE /v1/account/api-key x-api-key, revokes itself Statuses: pending -> awaiting_deposit -> confirming -> exchanging -> finished | refunded | failed Amounts are decimal strings in whole units. Errors are {code,message,...extra}. Docs https://api-allways.venturalabs.ai/v1/docs · Reference https://api-allways.venturalabs.ai/v1/llms.txt · Schema https://api-allways.venturalabs.ai/v1/openapi.json · App https://allways.venturalabs.ai