AgentPay API
Call paid APIs per request from a prepaid wallet. For what AgentPay is and what it costs, see the AgentPay overview.
Base URL
https://agentpay.myuiaf.com
All responses from AgentPay itself are JSON. Responses from a route are passed through as the API sent them.
MCP server
https://agentpay.myuiaf.com/mcp
The same features as MCP tools (list_routes, call_route, wallet_balance and list_transactions), for Claude, OpenAI, Gemini, Grok and any MCP client. Streamable HTTP; send your key as Authorization: Bearer fv1.... Setup examples: Integrations.
Authentication
Use the same API key as Freshness Validator. It looks like fv1.<agent_id>.<key_id>.<secret>. Send it in any one of these headers:
X-Agent-API-Key: fv1.acme-bot.k1.xxxxx
X-Api-Key: fv1.acme-bot.k1.xxxxx
Authorization: Bearer fv1.acme-bot.k1.xxxxx
Use X-Agent-API-Key if the API you are calling needs its own Authorization header from you. Your key is never sent to the API. To get a key, buy a credit pack or see Buying credits. Repeated failed attempts are throttled by IP, across both services.
List routes
GET /v1/routes
The APIs you can call, with the price per successful call and the time limit.
{
"routes": [
{
"id": "rt_weather",
"name": "Weather",
"description": "Forecasts by city",
"pricing_model": "markup_5_percent",
"price_per_call_usd": 0.03,
"sla_ms": 800,
"proxy_url": "https://agentpay.myuiaf.com/v1/proxy/rt_weather/"
}
]
}
Call a route
ANY /v1/proxy/{route_id}/{path} GET, POST, PUT, PATCH, DELETE, HEAD
Everything after the route id, and the query string, is appended to the API's base URL. The method, body (up to 1 MB) and your other headers are forwarded. AgentPay adds the API's key and removes your key, cookies and proxy headers.
curl -X POST "https://agentpay.myuiaf.com/v1/proxy/rt_weather/forecast?units=metric" \
-H "X-Agent-API-Key: fv1.acme-bot.k1.xxxxx" \
-H "Idempotency-Key: 7f3c9a1e-forecast-honolulu" \
-H "content-type: application/json" \
-d '{"city":"Honolulu","days":3}'
Redirects from the API are returned to you, not followed, so the API key is never sent to another host.
Idempotency
Every call needs an Idempotency-Key header: 1 to 255 printable characters, unique for each logical call. A UUID works well. Keys are remembered for 24 hours.
| You send a key that | Result |
|---|---|
| Is new | The call is made. |
| Already completed | The original response is returned again with Idempotent-Replayed: true. No new call, no new charge. Bodies over 64 KB are not kept: you get 409 replay_body_unavailable instead. |
| Is still in progress | 409 request_in_progress. Wait and retry. |
| Was used for a different request | 422 idempotency_key_reused. The method, path, query and body must match. |
| Failed before (not charged) | The call is tried again. |
When you are charged
The route's price is reserved from your available balance before the call, then:
| What happened | You get | Charged |
|---|---|---|
| API answered 2xx within the time limit | The API's response | Route price |
| API answered with any other status | The API's response, as sent | Nothing |
| No full answer within the time limit | 504 sla_exceeded | Nothing |
| API unreachable | 502 destination_unreachable | Nothing |
| Response larger than 10 MB | 502 response_too_large | Nothing |
| Not enough available credit | 402 insufficient_credits, call not made | Nothing |
| Wallet service unavailable | 503 ledger_unavailable, call not made | Nothing |
Money reserved for a call in progress is not available to other calls on either service until the call settles. A reservation that is never settled is released automatically, uncharged.
Response headers
| Header | Meaning |
|---|---|
x-agentpay-call-id | The call's id in your transaction list |
x-agentpay-charged-usd | Amount charged for this call (0 if not charged) |
x-agentpay-available-usd | Available balance after the call |
x-agentpay-ttfb-ms | Time until the API started answering |
x-agentpay-total-ms | Total time for the API's full answer |
idempotent-replayed | true on a replayed response |
Balance
GET /v1/wallet/balance
{
"agent_id": "acme-bot",
"tier": "early_testing",
"balance_usd": 4.97,
"held_usd": 0.03,
"available_usd": 4.94,
"shared_with": "Freshness Validator (api.myuiaf.com)",
"topup_url": "https://api.myuiaf.com/api/v1/credits"
}
held_usd is reserved for calls in progress. available_usd is what new calls can use.
Transactions
GET /v1/transactions ?limit=50&before=<call_id>
Your AgentPay calls, newest first, up to 200 per page. Pass next_before from one page as before to get the next.
{
"transactions": [
{
"call_id": "001762570000000-a1b2c3",
"idempotency_key": "7f3c9a1e-forecast-honolulu",
"route_id": "rt_weather",
"amount_usd": 0.03,
"status": "completed",
"reason": null,
"destination_response_code": 200,
"ttfb_ms": 171,
"total_ms": 184,
"ledger_tx_id": "...",
"created_at": "2026-11-08T03:00:00.000Z",
"settled_at": "2026-11-08T03:00:00.190Z"
}
],
"next_before": null,
"next_url": null
}
status is completed (charged), failed (not charged; reason says why) or pending.
Buying credits
AgentPay uses your Freshness Validator wallet. People can buy a credit pack by card. Agents can buy their own credits with POST https://api.myuiaf.com/api/v1/credits, by card or in USDC: see Buying credits. A 402 from AgentPay includes the price of the call and this address.
Limits
| Limit | Value |
|---|---|
| Requests per minute | 60, 120, 300 or 1,000 by tier, counted separately from Freshness Validator |
| Request body | 1 MB |
| Response body | 10 MB |
| Replayed response kept | 64 KB, for 24 hours |
| Time limit | Set per route (shown as sla_ms) |
Errors
AgentPay's own errors look like this:
{ "error": "This call costs $0.03; the available wallet balance is $0.01", "code": "insufficient_credits",
"price_usd": 0.03, "topup": { "url": "https://api.myuiaf.com/api/v1/credits", "method": "POST" } }
| Status | Code | Meaning |
|---|---|---|
| 400 | idempotency_key_required | Missing or invalid Idempotency-Key |
| 400 | invalid_path | The path tries to leave the route |
| 401 | missing_api_key, invalid_api_key | No key, or a wrong or revoked key |
| 402 | insufficient_credits | Not enough available credit for this call |
| 404 | route_not_found | No active route with this id |
| 409 | request_in_progress | Same key still running |
| 409 | replay_body_unavailable | Completed call whose response was too large to keep |
| 413 | body_too_large | Request body over 1 MB |
| 422 | idempotency_key_reused | Same key, different request |
| 429 | rate_limited, temporarily_blocked | Too many requests, or too many failed sign-ins; see Retry-After |
| 502 | destination_unreachable, response_too_large | Not charged |
| 503 | ledger_unavailable, routes_unavailable, route_misconfigured | Temporary; not charged. Retry with the same key. |
| 504 | sla_exceeded | Over the route's time limit; not charged |
Health
GET /v1/health
No key needed. Returns 200 with "status": "ok" when routes, keys and the wallet are all reachable, and 503 otherwise.