Wallet Automation

SIWE signup, wallet-managed keys, and x402 billing for scripts that run from a wallet.

Starts with
SIWE challenge
Returns
Fresh API keys
Billing
x402 on Base

Wallet automation covers SIWE signup, wallet-managed keys, and x402 billing.

Dashboard-managed X-API-Key is still the simplest option. Use SIWE and x402 when the code itself owns signup, key lifecycle, or payment without a browser session.

Where wallet auth fits

Wallet auth solves a specific job: the code owns signup, keys, or billing.

Use this if

The code owns signup, keys, or billing

This is the right fit when a script has to create an account, list or revoke keys, or upgrade tiers without the dashboard.

Skip it if

A normal API key already solves the job

Dashboard-managed keys are simpler. Use wallet auth when you need wallet ownership, not just because it exists.

Runbook

Go from SIWE challenge to live API key, then add x402 if you need a paid tier.

  1. 1
    0xArchive

    Get a SIWE challenge

    POST /auth/web3/challenge with the agent's wallet address. Returns a message to sign.

    POST /v1/auth/web3/challenge

  2. 2
    Wallet

    Sign the SIWE message locally

    The private key never leaves the wallet runtime. Only the signature is sent over the wire.

    personal_sign

  3. 3
    0xArchive

    Create the account and first key

    Submit the signed message to /web3/signup and receive an API key you can use immediately.

    POST /v1/web3/signup

  4. 4
    operator

    Call a live route with X-API-Key

    Use the returned key on the same REST, CLI, or AI-client paths as any dashboard-managed key.

    GET /v1/hyperliquid/summary/BTC

  5. 5
    0xArchive

    Request an x402 quote when needed

    POST /web3/subscribe with the target tier. The server returns a 402 payment quote with Base USDC details.

    POST /v1/web3/subscribe

  6. 6
    Wallet

    Sign the USDC authorization

    Create the EIP-3009 transferWithAuthorization payload locally, then retry the subscribe call with the payment signature header.

    payment-signature header

  7. 7
    0xArchive

    Switch to the returned key

    Every paid subscribe response returns a fresh API key. Downstream clients and scripts should adopt the new key immediately.

    { api_key, tier, expires_at, tx_hash }

Operational jobs

Publicly supported jobs: bootstrap, key lifecycle, and paid-tier changes.

Bootstrap account + key

Goal
Create the account and get the first usable API key from a wallet alone.
Command or route
POST /v1/auth/web3/challenge -> sign message -> POST /v1/web3/signup
Expected output
A live api_key and tier are returned immediately.
Next step
Use the key with REST, CLI, or AI Clients exactly as you would a dashboard-managed key.
Open AI Clients

List or revoke keys

Goal
Inspect active keys or invalidate one without opening the dashboard.
Command or route
POST /v1/web3/keys
POST /v1/web3/keys/revoke
Expected output
Current key inventory or a revoked key ID you can audit downstream.
Next step
Rotate scripts and clients to the surviving key before the revoked one is used again.
Open Authentication

Upgrade or renew with x402

Goal
Move to Build or Pro, renew the current tier, or queue a lower tier after expiry rules are applied.
Command or route
POST /v1/web3/subscribe -> receive 402 -> sign EIP-3009 authorization -> retry with payment-signature
Expected output
A fresh api_key, updated tier, expiry, and tx_hash prove the change.
Next step
Adopt the returned key in AI Clients or CLI so the upgraded entitlement is the one actually in use.
Open CLI

Code examples

Get signup and first-key creation working first. Add x402 after that.

TypeScript signup

TypeScript
import { privateKeyToAccount } from 'viem/accounts';
import { createWalletClient, http } from 'viem';
import { mainnet } from 'viem/chains';
const API = 'https://api.0xarchive.io/v1';
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
// Step 1: Get SIWE challenge
const { message, nonce } = await fetch(`${API}/auth/web3/challenge`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address: account.address }),
}).then(r => r.json());
// Step 2: Sign and submit
const signature = await account.signMessage({ message });
const { api_key, tier } = await fetch(`${API}/web3/signup`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, signature }),
}).then(r => r.json());
console.log(`API key: ${api_key} (tier: ${tier})`);

Python signup

Python
from eth_account import Account
from eth_account.messages import encode_defunct
import requests, os
API = "https://api.0xarchive.io/v1"
account = Account.from_key(os.environ["PRIVATE_KEY"])
# Step 1: Get SIWE challenge
resp = requests.post(f"{API}/auth/web3/challenge",
json={"address": account.address})
data = resp.json()
# Step 2: Sign and submit
sig = account.sign_message(
encode_defunct(text=data["message"]))
resp = requests.post(f"{API}/web3/signup",
json={"message": data["message"],
"signature": sig.signature.hex()})
result = resp.json()
print(f"API key: {result['api_key']} (tier: {result['tier']})")

x402 subscribe

Python
# Step 1: Request pricing (returns 402)
resp = requests.post(f"{API}/web3/subscribe",
json={"tier": "build"})
# resp.status_code == 402
payment_info = resp.json()["payment"]
# { amount: "49000000", asset: "USDC", network: "eip155:8453",
# pay_to: "0x...", asset_address: "0x8335..." }
# Step 2: Sign EIP-3009 transferWithAuthorization
# (see x402 spec for full signing flow)
# Then retry with payment header:
resp = requests.post(f"{API}/web3/subscribe",
headers={"payment-signature": base64_payment},
json={"tier": "build"})
result = resp.json()
# { api_key: "0xa_live_...", tier: "build",
# expires_at: "2026-04-06T...", tx_hash: "0x..." }

Endpoints

MethodPathDescription
POST/v1/auth/web3/challengeGet a SIWE nonce to sign (10 min TTL)
POST/v1/web3/signupCreate account + first API key from a signed SIWE message
POST/v1/web3/keysList active API keys for the wallet
POST/v1/web3/keys/revokeRevoke an API key by ID with a fresh signed request
POST/v1/web3/subscribeSubscribe to Build/Pro via x402 (USDC on Base) and return a fresh API key

Pricing

SIWE signup is free. Paid tiers are optional and activate through x402.

TierPriceDurationPayment
Free$0UnlimitedSIWE signup only
Build$49 USDC30 daysx402 (USDC on Base)
Pro$199 USDC30 daysx402 (USDC on Base)

Renewals and tier changes

A script can call /web3/subscribe at any time. The server decides whether the purchase renews, upgrades, or queues.

ScenarioExampleBehavior
Same-tier renewalBuild (15 days left) → Buy Build againExtends from current expiry. New expiry = old expiry + 30 days (45 days total remaining). A new API key is returned.
Upgrade to higher tierBuild (15 days left) → Buy ProApplies immediately. Remaining Build days carry forward: new expiry = old expiry + 30 days. Unused credits from Build are added as bonus credits on Pro. A new API key is returned.
Lower-tier purchase (active plan)Pro (15 days left) → Buy BuildQueued. You keep Pro for remaining 15 days, then automatically transition to Build for 30 days. A new API key is returned.
Purchase after expiryBuild (expired) → Buy Build or ProApplies immediately. New period starts from now for 30 days. A new API key is returned.
Free → PaidFree → Buy BuildApplies immediately. 30-day subscription starts from now. A new API key is returned.

The expires_at field always reflects the updated post-purchase state. Every purchase returns a fresh API key.

Security

Keep the security model boring and explicit: fresh signatures, local private keys, domain validation, and no session tokens.

  • No reusable sessions — every request requires a fresh SIWE signature, so the server does not mint a reusable auth token.
  • Single-use nonces — challenge nonces are consumed atomically, address-bound, and expire after 10 minutes.
  • Domain-locked validation — SIWE messages are validated against the server domain to reduce replay risk.
  • Private key stays local — only signatures and payment payloads cross the network.
  • Shared auth guardrails — wallet endpoints still sit behind the auth rate limiter, so scripts must handle retries and failures conservatively.

Where to use wallet auth next