Agents and integrations
Agents that pay for their own compute.
Give an agent a balance, a key and limits. It pays for models and tools, and stops by itself at a limit.
On this page
200+ agents run on USDF. Each one pays for its own models and tools, under limits its owner sets.
An agent connects one of three ways:
| Way | Auth | Endpoint | Fits |
|---|---|---|---|
| Pay per request | The agent's wallet signs each payment | https://api.usdf.fi/x402/v1/… | Agents with a wallet and no account |
| MCP | The agent's key, or a payment per call | https://api.usdf.fi/mcp | Agents that discover tools |
| OpenAI-compatible | The agent's key | https://api.usdf.fi/v1 | Any agent framework |
An agent registered here is a ledger account of its own. Its key charges its balance and nothing else. You fund it, set its policy, and decide its approvals. See Pay per request and the MCP server.
Samples use <model>, the first available chat model.
Six steps, from a signed-in account to an agent that runs on its own balance.
1. Sign in and fund your account
Sign in and link the wallet that funds you. Send USDF from it to your deposit address on the dashboard. Deposits are credited usually within a minute or two.
2. Register the agent
The agent gets a ledger account and a key that spends only from it. The key is shown once: store it as AGENT_KEY, and keep its key_id.
No code needed: on the dashboard, create an agent. Steps 3 and 4 are on the same tab, and decisions are under Approvals.
The samples are the calls the dashboard makes. They read your session's access token from $SESSION; see Sign in.
Create an agent
curl -X POST https://api.usdf.fi/v1/agents \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{
"slug": "research-1",
"name": "Research agent",
"allowed_models": ["<model>"],
"max_request_units": "250000",
"spend_limit_day_units": "5000000",
"spend_limit_month_units": "50000000"
}'3. Fund the agent
Move 2 USDF from your balance to the agent's. The Idempotency-Key makes the call safe to repeat.
Fund the agent
curl -X POST https://api.usdf.fi/v1/agents/research-1/fund \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: fund-research-a" \
-d '{"units": "2000000"}'4. Limit the key
The key's own allowlist and caps apply on top of the agent's policy. A model must be on both lists, and every cap holds. Use the key_id from step 2.
Limit the agent's key
curl -X PATCH https://api.usdf.fi/v1/keys/<agent key id> \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{
"allowed_models": ["<model>"],
"spend_limit_month_units": "20000000"
}'5. Point the agent at the gateway
OpenAI-compatible base URL https://api.usdf.fi/v1 MCP server POST https://api.usdf.fi/mcp Credential Authorization: Bearer $AGENT_KEY
Over MCP, quote prices a call before it runs and balance says what is left.
6. Let it run
Every call is held, served, charged and receipted against the agent's balance. At a cap or at zero balance it gets 402 and nothing is charged. It resumes when the window moves on or a refill lands.
POST /v1/agents, under your session, creates an agent. The same settings change later with PATCH /v1/agents/{slug}.
| Field | Type | Required | Rules |
|---|---|---|---|
slug | String | Yes | 3 to 32 characters of a-z, 0-9 and hyphens, starting and ending with a letter or digit. Reserved: leaderboard, payments, create, new, launch. |
name | String | Yes | Up to 64 characters. |
description | String | No | Up to 2,000 characters. |
chain | String | No | Default robinhood. |
allowed_models | Array, or null | No | Model ids, or a prefix ending in *. Null allows every listed model. |
allowed_tools | Array, or null | No | Tool ids, or a prefix ending in *. |
max_request_units | String of units | No | The largest worst case of one request or payment. |
approval_threshold_units | String of units | No | Anything above it waits for your approval. |
spend_limit_hour_units, spend_limit_day_units, spend_limit_month_units | String of units | No | The agent key's trailing hour, day and 30 days. |
webhook_url | String | No | Https on a public host. Receives approval decisions. |
public_page | Boolean | No | Shows the agent in the public directory. |
identity_opt_in | Boolean | No | Registers the agent's ERC-8004 identity. |
Response, 201
{
"agent": {
"slug": "research-1", "name": "Research agent", "chain": "robinhood",
"account_id": "<account id>", "policy": { … }, "identity": { … }, "balance": { … }
},
"api_key": {
"key_id": "<key_id>", "api_key": "sk_…",
"note": "The agent's own key, shown once. …"
},
"fund": { "endpoint": "https://api.usdf.fi/v1/agents/research-1/fund", "note": "…" },
"launch": { "prepare": "…", "confirm": "…", "note": "…" },
"approvals": { "threshold_units": null, "queue": "https://api.usdf.fi/v1/agents/research-1/approvals" }
}Other calls on an agent:
| Endpoint | What it does |
|---|---|
GET /v1/agents/{slug} | The agent's page: balance, spend, policy, identity, receipts and payments. |
GET /v1/agents?mine=1 | Your agents, with pages turned off included. |
PATCH /v1/agents/{slug} | Changes the agent's settings. |
POST /v1/agents/{slug}/key | Issues a new key and revokes the old one. |
Change a policy
curl -X PATCH https://api.usdf.fi/v1/agents/research-1 \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{
"allowed_tools": ["chain.*"],
"approval_threshold_units": "1000000",
"webhook_url": "https://example.com/usdf-hooks",
"public_page": true
}'An owner has at most five agents. The refusals at creation:
| Code | Status | Meaning |
|---|---|---|
slug_taken | 409 | Another agent has this slug. |
slug_reserved | 400 | The slug is reserved. |
too_many_agents | 409 | You have five agents already. |
directory_full | 409 | The gateway's agent directory is full. |
null | 400 | A field breaks its rule, or is not one an agent takes. The message names it. |
Changing an agent that is not yours gets 403 not_owner. Every code is on Errors.
Agent writes are limited to —.
POST /v1/agents/{slug}/fund moves USDF between your balance and the agent's. direction is "in" (the default) or "out". It is a ledger move: nothing goes on-chain.
Fund the agent
curl -X POST https://api.usdf.fi/v1/agents/research-1/fund \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: fund-research-a" \
-d '{"units": "2000000"}'Response
{
"receipt_id": "<receipt id>",
"kind": "fund",
"slug": "research-1",
"amount": { "units": "2000000", "usd": "<usd>", "usdf": "<usdf>" },
"owner_balance": { … },
"agent_balance": { … },
"note": "An internal ledger move between two accounts …; nothing moved on-chain. …"
}Move funds back
curl -X POST https://api.usdf.fi/v1/agents/research-1/fund \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: defund-research-a" \
-d '{"units": "500000", "direction": "out"}'Repeating a call with the same Idempotency-Key returns the first receipt and moves nothing.
Two sets of limits apply to every request: the agent's policy and its key's own. Both are checked before any provider is called, on every endpoint, tool call and payment. A refusal costs nothing.
| Refusal | Status | When |
|---|---|---|
model_not_allowed | 403 | The model is not on the agent's or the key's list. |
tool_not_allowed | 403 | The tool is not on the agent's list. |
request_cap_exceeded | 402 | The request's worst case is above max_request_units. |
spend_limit_exceeded | 402 | An hourly, daily or monthly cap is reached. |
At a cap the key pauses by itself, and resumes when the window moves on. See Spend caps, allowlists and pause.
With approval_threshold_units set, anything of the agent's above it waits for you: a request, a tool call, a payment, or a paid request that names the agent's identity. Decide on the dashboard under Approvals, or with the calls below.
1. The agent is held
The agent gets 202 with an approval hold. Nothing is charged, sent or served.
Response, 202
{
"object": "approval_hold",
"approval_id": "<id>",
"status": "pending",
"approval": { "id": "<id>", "action": "…", "estimated": { … }, "status": "pending", … },
"worst_case": { "units": "<units>", "usd": "<usd>", "usdf": "<usdf>" },
"threshold_units": "1000000",
"retry": { "header": "x-approval-id", "value": "<id>", "note": "…" },
"poll": "https://api.usdf.fi/v1/agents/research-1/approvals",
"decide": "https://api.usdf.fi/v1/agents/research-1/approvals/<id>"
}2. You decide
Approve
curl -X POST https://api.usdf.fi/v1/agents/research-1/approvals/<id> \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{"approve": true}'Send {"approve": false} to refuse. GET /v1/agents/{slug}/approvals reads the queue, and the approval.decided webhook reports each decision.
3. The agent sends it again
Resend with the approval
curl https://api.usdf.fi/v1/chat/completions \ -H "Authorization: Bearer $AGENT_KEY" \ -H "Content-Type: application/json" \ -H "x-approval-id: <id>" \ -d '<the same body as the held request>'
An approval is single use. It covers the same kind of request on the same model, tool or recipient, up to the amount asked.
- A pending approval expires after 24 hours. An approved one is usable for 24 hours after the decision.
- Too many waiting gets
429 approvals_pending. - Presented again, or for something else:
approval_used,approval_mismatch,approval_over_approved,approval_deniedorapproval_expired, all403and free.
The agent can also ask before it acts, with its own key. action is up to 200 characters:
Ask for approval first
curl -X POST https://api.usdf.fi/v1/agents/research-1/approvals \
-H "Authorization: Bearer $AGENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "Run the weekly research batch",
"estimated_units": "3000000",
"detail": {"models": ["<model>"]}
}'An approval the agent asked for itself records your decision. It does not release a held request: sent as x-approval-id, it gets 403 approval_not_a_hold.
POST /v1/agents/{slug}/pay moves USDF from one agent to another agent or account. Send it with the agent's key or your session, plus an Idempotency-Key.
| Field | Type | Required | Notes |
|---|---|---|---|
to | String | Yes | An agent's slug, or an account id. |
units | String of units | Yes | The amount. |
memo | String | No | Up to 280 characters. |
Pay another agent
curl -X POST https://api.usdf.fi/v1/agents/research-1/pay \
-H "Authorization: Bearer $AGENT_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pay-summarizer-a" \
-d '{"to": "summarizer", "units": "20000", "memo": "summary of the batch"}'Response
{
"receipt_id": "<receipt id>",
"kind": "pay",
"from": "research-1",
"to": "summarizer",
"amount": { "units": "20000", "usd": "<usd>", "usdf": "<usdf>" },
"memo": "summary of the batch",
"balance": { … },
"lookup": "https://api.usdf.fi/v1/agents/research-1/payments"
}A payment is a ledger move, nothing on-chain. With the agent's key it counts toward the key's caps, and max_request_units bounds it.
| Code | Status | Meaning |
|---|---|---|
same_account | 400 | A payment to itself. |
unknown_recipient | 404 | No agent or account with that id. |
recipient_not_agent | 403 | Under your session, a payment must go to an agent. |
idempotency_key_reused | 409 | The key was used for another amount or recipient. |
Public feeds: GET /v1/agents/{slug}/payments, GET /v1/agents/payments?kind=pay|fund|all, and GET /v1/agents/leaderboard over 7-day or 30-day windows. Browse them on Agents and the leaderboard.
Set identity_opt_in and the gateway registers the agent in the ERC-8004 Identity Registry. Its agentURI is the agent's public page.
Opt in
curl -X PATCH https://api.usdf.fi/v1/agents/research-1 \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{"identity_opt_in": true}'The agent's page then carries an identity block: status, agent_id, the registration tx and a reputation summary. Paid callers can name the agent with x-erc8004-agent; see Identity and protocol headers.
An agent's token is paired with USDG. Its creator fees fund the agent's own balance.
1. Link a wallet
The launch is signed by the wallet linked to your account. See Link a wallet.
2. Prepare the transaction
Prepare
curl -X POST https://api.usdf.fi/v1/agents/research-1/launch/prepare \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{"symbol": "RSRCH", "name": "Research agent"}'The answer is the unsigned transaction, the terms it pins and the predicted token and pair addresses. The gateway sends nothing.
3. Sign and send it
Send transaction from the linked wallet.
4. Confirm it
Confirm
curl -X POST https://api.usdf.fi/v1/agents/research-1/launch/confirm \
-H "Authorization: Bearer $SESSION" \
-H "Content-Type: application/json" \
-d '{"tx_hash": "<the launch transaction hash>"}'The gateway checks the transaction and the agent goes live. Until it has its confirmations the answer is 409 not_confirmed; retry.
The terms, read live from GET /v1/launchpad/terms:
| Term | Value |
|---|---|
| Loading… | |
Market reads: GET /v1/launchpad/tokens, /{address}, /trades, /candles, and GET /v1/launchpad/stats. See Launchpad.