# AI agent access

This site provides an OpenAI-compatible model API backed by a prepaid USD
balance. It runs on new-api by QuantumNous. Agents can sign in with an EVM
wallet and pay native USDC on Arbitrum One without browser automation, or buy
credits over the x402 payment protocol with no account at all.

## Discover

Read `/.well-known/agent.json` on this origin first. Its `available` and
`registration_available` fields describe the current configuration; follow
its endpoint links and `/api/agent/openapi.json` for request/response schemas.
The homepage links these documents without requiring JavaScript.

You need a wallet you are authorized to use. This release supports externally
owned EVM accounts with `personal_sign`, direct ERC-20 transfers with gas paid
in ETH on Arbitrum, and x402 `exact` payments in USDC on the networks named in
the manifest. It does not support exchange deposits, smart-contract wallets,
bridged USDC.e, or other currencies.

## Buy credits with x402 (no account needed)

The fastest route to a working key. `POST` to a credit tier with no body and no
authentication:

    POST /api/x402/credits/0.10
    POST /api/x402/credits/1
    POST /api/x402/credits/5
    POST /api/x402/credits/20

The first call answers `402 Payment Required` with a `PAYMENT-REQUIRED` header
holding base64-encoded JSON: the accepted networks, the USDC contract, the exact
amount in atomic units, and the address to pay. Sign a transfer authorisation
for one of those options and send the request again with the base64-encoded
payload in a `PAYMENT-SIGNATURE` header. Any standard x402 client does this for
you.

On success the response carries `PAYMENT-RESPONSE` with the settlement and a
body containing `api_key`, `base_url` and the credited `balance_usd`. Use the
key immediately as `Authorization: Bearer sk-...` against `/v1`. The wallet that
paid owns the resulting account, so signing in later with the same wallet
through the challenge flow above reaches the same balance.

Payment is only captured when the transfer settles. A rejected signature, a
failed settlement or a server error all return without charging you. Retrying
a payment you already sent returns the original key rather than issuing a
second one.

### Signing by hand

Without an x402 client, sign an EIP-712 `TransferWithAuthorization` (EIP-3009)
for the USDC contract of the option you pick:

- domain: `name` and `version` from the option's `extra` (`USD Coin`, `2`),
  `chainId` from its `network` (8453 for `eip155:8453`, 42161 for
  `eip155:42161`), and its `asset` as `verifyingContract`
- message: `from` is your address, `to` is `payTo`, `value` is `amount`,
  `validAfter` is 0, `validBefore` is now plus `maxTimeoutSeconds`, and
  `nonce` is 32 random bytes
- signature: the 65 bytes r, s, v with v = 27 or 28, hex-encoded with `0x`

Send exactly the values you signed, with numbers as decimal strings. A
rejection with `invalid_exact_evm_signature` means the signature does not
recover to `from` for the fields sent, which is almost always a wrong domain
field. This Python example (`pip install eth-account requests`) buys the $0.10
tier on Base; the facilitator pays the gas, so the wallet needs only USDC:

    import base64, json, os, time, requests
    from eth_account import Account

    URL = "https://yiduochan.com/api/x402/credits/0.10"
    acct = Account.from_key(os.environ["WALLET_KEY"])

    offer = json.loads(base64.b64decode(requests.post(URL).headers["PAYMENT-REQUIRED"]))
    terms = next(a for a in offer["accepts"] if a["network"] == "eip155:8453")  # Base

    auth = {
        "from": acct.address,
        "to": terms["payTo"],
        "value": int(terms["amount"]),
        "validAfter": 0,
        "validBefore": int(time.time()) + terms["maxTimeoutSeconds"],
        "nonce": os.urandom(32),
    }
    signed = acct.sign_typed_data(
        domain_data={
            "name": terms["extra"]["name"],        # "USD Coin"
            "version": terms["extra"]["version"],  # "2"
            "chainId": int(terms["network"].split(":")[1]),
            "verifyingContract": terms["asset"],
        },
        message_types={"TransferWithAuthorization": [
            {"name": "from", "type": "address"},
            {"name": "to", "type": "address"},
            {"name": "value", "type": "uint256"},
            {"name": "validAfter", "type": "uint256"},
            {"name": "validBefore", "type": "uint256"},
            {"name": "nonce", "type": "bytes32"},
        ]},
        message_data=auth,
    )
    payment = {
        "x402Version": 2,
        "accepted": terms,
        "payload": {
            "signature": "0x" + bytes(signed.signature).hex(),
            "authorization": {
                "from": auth["from"], "to": auth["to"], "value": str(auth["value"]),
                "validAfter": str(auth["validAfter"]), "validBefore": str(auth["validBefore"]),
                "nonce": "0x" + auth["nonce"].hex(),
            },
        },
    }
    resp = requests.post(URL, headers={
        "PAYMENT-SIGNATURE": base64.b64encode(json.dumps(payment).encode()).decode()})
    print(resp.status_code, resp.json())

## Register or sign in

1. `POST /api/agent/auth/challenge` with `{"wallet":"0xYOUR_ADDRESS"}`.
2. Verify the returned message names this site's origin, your address,
   Arbitrum chain ID `42161`, a nonce, and an expiry. Sign the exact `message`
   bytes with your wallet's EIP-191 `personal_sign` / `signMessage` function.
   Signing this login message does not move funds.
3. `POST /api/agent/auth/verify` with `flow_token` and the hex `signature`.
   The first successful login creates an ordinary account with **zero**
   credit. Existing accounts can log in while new registration is closed.
4. Save the returned `access_token` securely. Use `Authorization: Bearer TOKEN`
   for `/api/agent/*` account endpoints. It expires in 15 minutes. On HTTP 401,
   request and sign a fresh challenge. Never reuse a consumed challenge.

This token manages only your own agent account, API keys and deposits. It is
not a dashboard login or a model API key. No email, password or CAPTCHA is
required for this explicitly enabled wallet-registration route.

## Inspect models, prices and balance

- `GET /api/agent/me` returns your balance in USD and internal quota units.
- `GET /api/agent/pricing` returns the existing model catalog and pricing,
  filtered to the account's usable groups. Model prices can change; inspect
  the current response before selecting a model or spending budget.
- After creating a model API key, `GET /v1/models` lists models available to it.

## Deposit USDC

1. `POST /api/agent/deposits` with `{"amount_usdc":"10"}` and a unique
   `Idempotency-Key` header (8–128 characters). Amounts must be whole USDC,
   minimum 1 and maximum 1000. One USDC credits one USD of API balance.
   Keep the same key and amount when retrying; do not create another order
   merely because a response was lost. At most ten unexpired unpaid orders
   can be open for one account.
2. Save the returned order before paying. Verify all payment terms:
   `network` is `eip155:42161`, `asset` is native USDC
   `0xaf88d065e77c8cC2239327C5EDb3A432268e5831`, `payer` is your wallet,
   `pay_to` is the site's receiver, and `amount_atomic` matches your authorized
   budget at six decimal places. The response provides a ready-to-sign
   ERC-20 `transfer` transaction. **The transaction's `to` is the USDC
   contract; the merchant address is encoded in its `data`.** Its native ETH
   `value` is zero. Your wallet estimates and pays the ETH network fee.
3. Send that transaction once on Arbitrum One from the same wallet used to
   sign in. The order accepts blocks from `start_block` onward, and the
   transfer must be included before `expires_at`. Old deposits cannot be
   reused. One transaction can pay only one order. Do not batch transfers.
4. Persist the transaction hash and `POST /api/agent/deposits/ORDER/confirm`
   with `{"transaction_hash":"0x..."}`. The hash is only a lookup hint;
   the site verifies the successful USDC Transfer event on-chain.
5. HTTP 202 with `transaction_pending` means the receipt is not yet available:
   retry **confirmation with the same hash** after `Retry-After` seconds.
   HTTP 202 with `order.status=confirming` means the payment was verified and
   saved. Poll the order's status link every 30 seconds until `paid`. A
   background reconciler also completes saved payments after server restarts.
6. Credit is granted only when the receipt block is canonical and at or below
   the Arbitrum RPC `finalized` head. This can take many minutes. An order can
   become paid after its expiry if the transfer itself was included on time.
   On a temporary RPC/server error, retry; **do not send another payment**.

The server checks chain, token contract, authenticated sender, recipient,
exact amount, successful receipt, order window and finality. Duplicate
confirmation requests cannot add credit twice. Sending an unrelated transfer
to the wallet without an order and confirmation is not an automatic deposit.
If an order remains unresolved, retain its order ID and transaction hash for
support; do not discard or replace that payment record.

## Create a bounded model API key

`POST /api/agent/tokens` with:

```json
{"name":"My agent","budget_usd":"10","expires_in_seconds":86400,"models":["MODEL_ID"]}
```

`budget_usd` is a positive whole number up to 10000. The key lifetime is
60 seconds to 30 days (default one day). `models` is optional; omitting it
allows the account's available models. The response reveals `api_key` once.
Keep it securely. Issuing a key does not reserve or add money: calls are
limited by both the key's remaining budget and the account's real balance.
Use `GET /api/agent/tokens` to inspect up to 100 keys and
`DELETE /api/agent/tokens/ID` to revoke an owned key.

## Call a model

Use the **model API key**, not the wallet-login access token:

```http
POST /v1/chat/completions
Authorization: Bearer sk-YOUR_MODEL_API_KEY
Content-Type: application/json

{"model":"MODEL_ID","messages":[{"role":"user","content":"Hello"}],"max_tokens":64}
```

The existing API also supports `/v1/responses` and other provider formats
where the selected model supports them. Standard model billing deducts
actual usage from the account and key budgets. Streamed calls use SSE when
`stream` is true. Choose a model from the live catalog rather than assuming
any particular model is configured.

## Retry and credentials

Account APIs return errors as `{"error":{"code":"...","message":"..."}}`.
Honor HTTP 429 and its `Retry-After` header. Refresh wallet authentication on
401. Correct request errors on 400, 404, 409 or 422 before retrying. HTTP 503
`chain_unavailable` is temporary and never proves a payment failed.

Keep the wallet's signing capability local to the agent's authorized wallet
provider. Never send a private key or seed phrase to this site. Deposit and
key-creation responses use `Cache-Control: no-store`. The machine-readable
manifest links the current terms, privacy policy and integration guide.
