Buy 500 USDC of bitcoin
Send 500 USDC on Algorand and receive bitcoin at the address you name. The quote — spot price, your rate, the spread and exactly how much bitcoin arrives — is returned before you pay and frozen into the order, so the number you agreed to is the number that gets sent. A person completes the transfer and records the bitcoin transaction id against your order.
POST https://api.bottrunk.com/s/btc-500
On request A person does this work. Write to us first so we can agree scope and timing; the endpoint answers 503 until then, so nothing can be charged by accident.
Call it
# once, on the machine your agent runs on npx bottrunk-mcp wallet # prints the address; send it 0.3 ALGO, then USDC claude mcp add bottrunk -- npx -y bottrunk-mcp # then ask your agent for it — the tool is `bottrunk_btc_500`
# Ask. You get a 402 with the payment requirements.
curl https://api.bottrunk.com/s/btc-500 \
-H "Content-Type: application/json" \
-d '{"btc_address":"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq","contact_email":"buyer@example.com"}'
# Pay and retry. Any x402 client does this for you.
curl https://api.bottrunk.com/s/btc-500 \
-H "PAYMENT-SIGNATURE: <signed USDC transfer>" \
-H "Content-Type: application/json" \
-d '{"btc_address":"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq","contact_email":"buyer@example.com"}'
# pip install "x402-avm[avm,requests]==2.0.2"
import base64, os
from algosdk import encoding, mnemonic
from x402 import x402ClientSync
from x402.http import x402HTTPClientSync
from x402.http.clients.requests import x402_requests
from x402.mechanisms.avm import ALGORAND_MAINNET_CAIP2
from x402.mechanisms.avm.exact import ExactAvmScheme
class MnemonicSigner: # signs the payment with your agent's 25-word mnemonic
def __init__(self, words):
self._sk = mnemonic.to_private_key(words)
self.address = encoding.encode_address(base64.b64decode(self._sk)[32:])
def sign_transactions(self, unsigned_txns, indexes_to_sign):
return [base64.b64decode(encoding.msgpack_encode(
encoding.msgpack_decode(base64.b64encode(t).decode()).sign(self._sk)))
if i in indexes_to_sign else None
for i, t in enumerate(unsigned_txns)]
# A wallet holding 0.3 ALGO, opted in to USDC, funded with the USDC it may spend.
signer = MnemonicSigner(os.environ["ALGORAND_MNEMONIC"])
client = x402ClientSync().register(ALGORAND_MAINNET_CAIP2,
ExactAvmScheme(signer, algod_url="https://mainnet-api.algonode.cloud"))
with x402_requests(client) as session: # answers the 402, pays, retries
r = session.post("https://api.bottrunk.com/s/btc-500", json={"btc_address": "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq", "contact_email": "buyer@example.com"})
print(r.json())
receipt = x402HTTPClientSync(client).get_payment_settle_response(r.headers.get)
print(receipt.transaction) # the settlement on-chain
// npm install @x402-avm/fetch @x402-avm/avm @x402-avm/core algosdk
import algosdk from "algosdk";
import { x402Client } from "@x402-avm/core/client";
import { ExactAvmScheme } from "@x402-avm/avm/exact/client";
import { toClientAvmSigner } from "@x402-avm/avm";
import { wrapFetchWithPayment } from "@x402-avm/fetch";
// A wallet holding 0.3 ALGO, opted in to USDC, funded with the USDC it may spend.
const { sk } = algosdk.mnemonicToSecretKey(process.env.ALGORAND_MNEMONIC!);
const signer = toClientAvmSigner(Buffer.from(sk).toString("base64"));
const client = new x402Client().register("algorand:wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=",
new ExactAvmScheme(signer, { algodUrl: "https://mainnet-api.algonode.cloud" })); // default is TestNet
const fetchWithPay = wrapFetchWithPayment(fetch, client);
const r = await fetchWithPay("https://api.bottrunk.com/s/btc-500", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({"btc_address":"bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq","contact_email":"buyer@example.com"}),
});
console.log(await r.json());
How it behaves
The decisions this service makes on your behalf, and the ones that end in a refusal. A 4xx is never settled, so a refusal costs you nothing.
| Arranged first, for now | This answers 503 until a licensed partner is behind it. Exchanging one asset for another is a regulated activity wherever the buyer is calling from, so nothing can be charged here until the compliance sits with someone authorised to carry it. Write first and we will tell you where that stands. |
| The spread is stated, not hidden | You are charged spot plus 10.0 %. The response shows the spot price, your rate and what the delivered bitcoin is worth at spot, so the cost of using this rather than an exchange is a number you can read before you pay. |
| The quote is frozen | Taken at the moment you order and recorded on it. If bitcoin moves between your payment and the send, the amount you were quoted is still the amount that arrives. |
| No fresh price, no quote | If we cannot get a bitcoin price under half an hour old, the call is refused and nothing is charged. A stale quote is a loss for one of us and we will not guess which. |
| The address is yours to get right | We check the shape of it and a person checks it again before sending, but bitcoin sent to a valid address you mistyped cannot be recalled. |
| Method | POST only. A GET on the same path returns this page. |
Who does the work
A person, not a script. The payment settles on-chain first; the job then enters our queue and is done within the stated window. You get an order id immediately and can poll it until the proof comes back — a bank receipt reference, photos, or the finished text. If we cannot deliver, the USDC is returned to the paying wallet and the refund transaction is recorded on the order.
Input
| btc_address | string | Where the bitcoin should arrive. |
| contact_email | string | Optional: emailed when it is sent. |
Output
| order_id | string | Token to poll at /orders/{order_id}. |
| quote | object | Spot, your rate, the spread, and the bitcoin that will arrive. |
| status | string | pending until sent, then delivered. |
| result | object | The bitcoin transaction id, once sent. |