Paymaster routing, the RPC gateway, apps, reports and webhooks. You are reading it.
Let an agent pay x402 resources under a limit it cannot raise. Spending limits, refusal codes, the ledger.
Overview
Tollbeam web client documentation
This page is intentionally scoped to what the current web client is actually wired to do. If a route, endpoint, or workflow is not described here, it is not currently integrated by this frontend.
The current app covers authentication, account profile management, password reset, application management, the public RPC integration example, transaction and budget reports, event logs, webhook configuration, and dashboard views for routing, billing, admin plan management, and admin account plan assignment.
- Create an account at /signup.
- Verify the email token sent to the email you registered with.
- Log in at /login.
- Create or edit apps in the dashboard at /dashboard/apps.
- Use the app API key with the production RPC gateway at /v1/base/rpc or /v1/arbitrum/rpc.
- Review reports, logs, and webhooks from the dashboard sidebar.
Auth and session
Base URL, public routes, and protected access
Two credentials, and they are not interchangeable
Everything below sends Authorization: Bearer <token>, but there are two different tokens, and using the wrong one returns a bare 401 unauthorized with no hint about which you needed.
- An app API key identifies one application. Use it for anything an agent or server does on behalf of that app: the RPC gateway, /v1/pay/fetch, /v1/budget. It is returned once when the app is created, and it cannot create or modify apps or policies — those are account-level actions and no app key can perform them, by design.
- A session token identifies a signed-in person. Use it for account and configuration work: POST /v1/apps, POST /v1/policies, the reports endpoints. Get one from POST /v1/auth/login, which returns the token in the response body — so a script can authenticate without a browser.
If creating an app or a policy returns 401 while your app key works fine everywhere else, this is why: the right key for the wrong layer.
# Get a session token for scripting
curl -s -X POST "$TOLLBEAM_API/v1/auth/login" \
-H "content-type: application/json" \
-d '{"email": "you@example.com", "password": "your-password"}'
# -> {"token": "<session token>", "expiresAt": "<iso8601>", "user": {}}
# Then use it for account-level calls
curl -s -X POST "$TOLLBEAM_API/v1/apps" \
-H "authorization: Bearer $SESSION_TOKEN" \
-H "content-type: application/json" \
-d '{"name": "my-app", "chains": []}'A session token stands in for a password, so treat it like one: out of logs, out of source control, and check expiresAt rather than discovering expiry mid-run. Note that apps created this way default to the production environment — there is no environment field on the create call.
How the dashboard uses this
Successful login stores a bearer token in local storage under tollbeam_token. Protected dashboard requests send that token in the Authorization header.
POST /v1/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "your-password"
}
// Expected response shape used by the frontend
{
"token": "jwt-or-session-token",
"expiresAt": "2026-03-24T12:00:00Z",
"user": {
"email": "user@example.com"
}
}POST /v1/auth/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "your-password",
"firstName": "Ada",
"lastName": "Lovelace",
"companyName": "MyWallet Labs",
"appName": "My First App",
"role": "developer",
"marketingOptIn": true,
"termsAcceptedAt": "2026-03-17T15:00:00.000Z"
}GET /v1/auth/verify?token=<email-token>
GET /v1/auth/me
POST /v1/auth/logout
POST /v1/auth/password-reset/request
{
"email": "user@example.com"
}
POST /v1/auth/password-reset/confirm
{
"token": "<token-from-email>",
"password": "NewPassword123"
}- /signup creates an account and records terms acceptance time.
- /login signs the user in and persists session state.
- /forgot-password requests a reset email.
- /reset-password?token=... confirms a new password.
- /dashboard/account/security can send a reset email for the signed-in user.
Apps API
Manage application records and keys
The Apps screen at /dashboard/apps lists apps and supports create, update, and delete flows. The frontend is currently wired to GET /apps, POST /apps, and PUT /apps/:id, DELETE /apps/:id, plus POST /apps/:id/rotate-key.
API keys are now treated as one-time secrets. The full key is only shown from the create response or the rotate response. After that, the UI only displays api_key_prefix and api_key_last4.
GET /apps
Authorization: Bearer <token>
// Frontend expects masked key fields from list/detail responses
[
{
"id": "app_123",
"name": "My Production App",
"status": "active",
"api_key_prefix": "abcd1234",
"api_key_last4": "9xyz"
}
]
POST /apps
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "My Production App",
"status": "active",
"daily_cap_wei": "5000000000000000000",
"rev_share_bps": 250,
"per_action_fee_minor": 100,
"allowlist": ["0x1234..."],
"denylist": ["0xabcd..."]
}
// Full API key is only returned here, on creation
{
"id": "app_123",
"name": "My Production App",
"status": "active",
"api_key": "FULL-KEY-ONLY-ONCE",
"api_key_prefix": "abcd1234",
"api_key_last4": "9xyz"
}
POST /apps/:id/rotate-key
Authorization: Bearer <token>
// Full replacement key is only returned here, on rotation
{
"api_key": "NEW_FULL_KEY_ONLY_ONCE",
"api_key_prefix": "wxyz5678",
"api_key_last4": "1abc"
}
PUT /apps/:id
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Renamed App",
"status": "paused"
}
DELETE /apps/:id
Authorization: Bearer <token>
{
"status": "deleted"
}RPC endpoint
Primary JSON-RPC gateway for client traffic
The primary client load target is POST /v1/:chain_name/rpc. The path segment is a supported chain slug, not a provider name or numeric chain ID. Production uses mainnet chain slugs such as base and arbitrum. This is the JSON-RPC gateway your AA client points to; it is not a dashboard CRUD endpoint.
Requests use your app API key in the Authorization: Bearer <app api_key> header. The supported methods are pm_sponsorUserOperation, eth_sendUserOperation and eth_getUserOperationReceipt, which returns null until an operation is included. Use production mainnet chain slugs such as base or arbitrum in the RPC path examples.
POST /v1/base/rpc
Authorization: Bearer <app api_key>
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "pm_sponsorUserOperation",
"params": ["<userOperation>", "<entryPoint>", "<context>"]
}
// JSON-RPC response shape
{
"jsonrpc": "2.0",
"id": 1,
"result": { "...": "..." }
}- Build the userOperation with its nonce and a placeholder signature: any 65 bytes, such as the one below. Most wallet SDKs do this for you.
- Call pm_sponsorUserOperation, then copy the returned paymasterAndData and gas fields into the operation.
- Sign the operation for real. The account signs over paymasterAndData, so this has to come after sponsorship.
- Send it with eth_sendUserOperation, passing the returned quoteId as the third parameter so the operation is matched to its sponsorship, then poll eth_getUserOperationReceipt.
With an empty signature, many accounts revert while the bundler simulates the operation, and sponsorship is refused with AA23 reverted: ECDSA: invalid signature length.
// A commonly used placeholder signature (65 bytes) 0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c
Gas sponsorship needs no signer and no deposit from you. Signers belong to agent payments, a separate product.
On Base, a new production app is sponsored from its first request on a starter allowance, with nothing to set up. The starter allowance has a daily limit and a total limit, lasts seven days, and handles one operation at a time: send the next once the previous one has landed. If your app needs more, contact us and we will give it a dedicated budget.
On Optimism and Arbitrum, we set a new app’s gas budget by hand after signup. Until it is set, sponsorship requests are refused with gas_budget_pending, so you know to wait rather than debug. If it has been more than a day, contact us.
Include the userOperation’s nonce when requesting sponsorship. Refusals specific to the starter allowance return JSON-RPC error -32003 with one of these messages:
- starter_operation_in_flight: one operation is already pending. Submit or abandon it, then retry.
- starter_daily_budget_exhausted: today’s starter allowance is used. It resets at midnight UTC.
- starter_lifetime_exhausted: the starter allowance is used up. Contact us for a dedicated budget.
- starter_capacity_exhausted: no starter capacity is free right now. Retry later.
- starter_operation_too_expensive: this operation could cost more than one starter operation allows. Lower its gas limits.
- starter_requires_verified_email: verify your account email first.
- nonce_required: the request had no usable nonce.
- gas_budget_pending: the app has no active gas budget on this chain yet. Not specific to the starter allowance.
- Use the API key returned once when an app is created or rotated in /dashboard/apps.
- The dashboard does not submit JSON-RPC calls itself; this endpoint is documented because the landing page and routing copy direct integrators to it.
- Current production route values are mainnet chain slugs such as base and arbitrum. Testnet route values, such as base-sepolia, are for non-production testing only where enabled.
Reports API
Transactions, budgets, and CSV export
The usage dashboard at /dashboard is currently backed by transaction and budget report endpoints. Transactions support limit, date, and appId query parameters. Budgets support appId. Both report types can be exported with format=csv.
GET /reports/transactions?limit=200&date=2026-03-17&appId=app_123
Authorization: Bearer <token>
// Frontend expects
{
"data": [
{
"id": "tx_01",
"appId": "app_123",
"chain": "Arbitrum One",
"chainId": 42161,
"txHash": "0x...",
"status": "sponsored",
"provider": "alchemy",
"sponsoredWei": "1000000000000000",
"feeMinor": "2000000000000000",
"createdAt": "2026-03-17T12:00:00Z"
}
],
"count": 1
}Budgets are gas sponsorship caps, denominated in wei. Only apps that have a daily cap set, or that have used gas today, are returned — an agent-payments app has no gas cap and is not listed here. Its spending limits live under Spending controls and are denominated in USD.
GET /reports/budgets?appId=app_123
Authorization: Bearer <token>
// Frontend expects
{
"data": [
{
"appId": "app_123",
"dailyCapWei": "5000000000000000000",
"usedTodayWei": "1200000000000000000",
"resetsAt": "2026-03-18T00:00:00Z"
}
],
"count": 1
}
GET /reports/transactions?format=csv&limit=200
GET /reports/budgets?format=csvLogs API
Filterable event log stream
The Event Logs page at /dashboard/logs fetches /logs with optional filters for app, event type, status, and date range. The UI accepts either a bare array or an object containing data.
GET /logs?appId=app_123&eventType=userop.sponsored&status=success&startDate=2026-03-01&endDate=2026-03-17 Authorization: Bearer <token>
- userop.sponsored
- userop.failed
- budget.exceeded
- balance.low
- rate.limited
Webhooks API
Per-app webhook config, testing, and secret rotation
The webhooks screen uses /webhooks/config to list app webhook settings, update a webhook URL, and rotate signing secrets. Test deliveries are sent through /webhooks/test.
GET /webhooks/config
Authorization: Bearer <token>
PUT /webhooks/config?appId=app_123
Authorization: Bearer <token>
Content-Type: application/json
{
"webhookUrl": "https://your-app.com/webhooks/tollbeam"
}
POST /webhooks/config/rotate-secret?appId=app_123
Authorization: Bearer <token>
POST /webhooks/test?appId=app_123
Authorization: Bearer <token>
Content-Type: application/json
{
"eventType": "userop.sponsored"
}- userop.sponsored
- userop.failed
- balance.low
Dashboard coverage
What the current web client exposes
Some dashboard pages in this repo are primarily UI surfaces rather than fully wired CRUD integrations. This section captures the current state so the docs stay accurate.
- Usage and reports: live transactions, budgets, CSV export, and app/date filtering are wired.
- Apps: list, create, update, and API key rotation are wired; delete is also supported from the edit flow. Full API keys are only revealed once on create or rotate, then only prefix and last4 are shown.
- Logs: filterable event stream with a detail modal is wired.
- Webhooks: list, URL update, test send, and secret rotation are wired.
- Account profile: PATCH /users/me is wired for profile updates.
- Account security: the page currently sends a password reset email for the signed-in user; it is not a direct in-app password change form.
- Routing and chains: the page is now a trimmed-back read-only reference that links to public status and docs. The dashboard is not wired to a dedicated routing admin API yet, and the documented production RPC routes use mainnet chain slugs, for example /v1/base/rpc and /v1/arbitrum/rpc.
- Billing: summary, public tiers, account plan changes, top-up checkout, subscription checkout, and the transaction ledger are wired in the current client.
- Admin billing plans: GET /billing/admin/tiers, PUT /billing/admin/tiers/:key, and DELETE /billing/admin/tiers/:key are wired for admin sessions.
- Admin users: GET /admin/users, GET /admin/users/:userId, PATCH /admin/users/:userId, and PUT /admin/users/:userId/enterprise-plan are wired for admin sessions.
- Admin plan assignment: GET /billing/admin/tiers and PATCH /billing/admin/accounts/:userId/tier are wired from the admin user detail screen, including the returned subscriptionMismatch warning state.
- Browser access: hosted frontend origins must be allowlisted by backend CORS settings such as FRONTEND_BASE_URL or CORS_ALLOWED_ORIGINS, or browser requests will fail with origin_not_allowed.
Changelog
Documentation releases
- October 9, 2026Version 1.1.0How a new app gets gas: the starter allowance on Base and its refusal messages, and that gas sponsorship needs no signer. An app without an active budget is refused up front with gas_budget_pending. eth_getUserOperationReceipt is now supported.
- March 17, 2026Version 1.0.0First complete docs pass aligned to the current frontend: auth flows, password reset, apps, hosted RPC example, reports, logs, webhooks, and dashboard coverage.