Account endpoints

The account surface is API-only: no web portal, everything via HTTP. Base URL for all endpoints on this page: https://toll.nanotoll.dev.

POST /api/v1/register

No auth, no body. Rate-limited per source IP.

Request: empty body.

Response (201 Created):

{
  "api_key": "macs_00000000000000000000000000000000",
  "funding_address": "0xeDfD6c7FB59c6B1dA3a3aABb30D6c96b868f0dA5",
  "chain": "base",
  "token": "USDC",
  "credit_balance": 0.0
}

The API key is returned exactly once and cannot be recovered. The funding address is a unique USDC address on Base, derived per account; incoming USDC transfers are credited to the account's balance (see billing).

Errors: 429 RATE_LIMITED (too many registrations from one IP), 500 INTERNAL_ERROR.

GET /api/v1/balance

Bearer auth. Not billed.

Request: no parameters.

Response (200):

{
  "credit_balance": 12.50,
  "funding_address": "0xeDfD6c7FB59c6B1dA3a3aABb30D6c96b868f0dA5",
  "chain": "base",
  "token": "USDC"
}

GET /api/v1/payments

Bearer auth. Not billed. Returns the onchain deposit history for the account: USDC transfers the watcher credited, most recent first. Per-call deductions are not itemized here; your current balance is on the balance endpoint.

Request (query parameters):

  • limit (optional): integer, how many records to return. Default 20, maximum 100.

Response (200):

{
  "payments": [
    {
      "tx_hash": "0x9e52adaf44e96394f2014201399406970299b164ba79d185e4986fede8bf5f86",
      "log_index": 0,
      "amount": 10.00,
      "chain": "base",
      "confirmations_seen": 15,
      "status": "confirmed",
      "created_at": "2026-10-20T14:00:00Z"
    }
  ],
  "count": 1
}

status is "confirmed" or "reorged" (rare: a deposit that was orphaned from the chain).

An account is exactly: one key, one funding address, one balance.

GET /api/v1/account reports key_status, derived on read: active, or expired for a free trial key past its expires_at. A paid or operator key has no expiry and is never retired - a credit_balance of 0 is an insufficient balance (HTTP 402), not a dead key. No key is ever deleted and no account is ever removed.

Free trial key

You can start without funding anything: a free trial key comes with a small starting credit and an expiry date. The barter is your email address, verified by a code. One trial key per email address and per IP address; disposable/temporary email domains are rejected. The verification code is what proves you own the mailbox - nothing is sent to the address beyond the verification mail.

POST /api/v1/trial

No auth. Rate-limited per source IP.

Request:

{
  "email": "you@example.com"
}

Response (200):

{
  "message": "verification code sent",
  "expires_in_minutes": 15
}

A 6-digit code is mailed to the address.

POST /api/v1/trial/verify

No auth. Submit the code from the mail.

Request:

{
  "email": "you@example.com",
  "code": "123456"
}

Response (200):

{
  "api_key": "macs_00000000000000000000000000000000",
  "credit_balance": 0.25,
  "key_type": "free",
  "expires_at": "2026-11-19T00:00:00Z"
}

Smaller than the registration response on purpose: a trial holder needs the key, the grant and the expiry. No deposit address, chain or token is issued here because nothing is payable yet - top up with a registered key, or read this key's funding address from GET /api/v1/account once you hold it. The trial key stops working at the expiry timestamp, and credit_balance shows the trial grant that was credited (the amount in this example is illustrative; the deployed grant applies).