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:

WayAuthEndpointFits
Pay per requestThe agent's wallet signs each paymenthttps://api.usdf.fi/x402/v1/…Agents with a wallet and no account
MCPThe agent's key, or a payment per callhttps://api.usdf.fi/mcpAgents that discover tools
OpenAI-compatibleThe agent's keyhttps://api.usdf.fi/v1Any 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
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
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
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}.

FieldTypeRequiredRules
slugStringYes3 to 32 characters of a-z, 0-9 and hyphens, starting and ending with a letter or digit. Reserved: leaderboard, payments, create, new, launch.
nameStringYesUp to 64 characters.
descriptionStringNoUp to 2,000 characters.
chainStringNoDefault robinhood.
allowed_modelsArray, or nullNoModel ids, or a prefix ending in *. Null allows every listed model.
allowed_toolsArray, or nullNoTool ids, or a prefix ending in *.
max_request_unitsString of unitsNoThe largest worst case of one request or payment.
approval_threshold_unitsString of unitsNoAnything above it waits for your approval.
spend_limit_hour_units, spend_limit_day_units, spend_limit_month_unitsString of unitsNoThe agent key's trailing hour, day and 30 days.
webhook_urlStringNoHttps on a public host. Receives approval decisions.
public_pageBooleanNoShows the agent in the public directory.
identity_opt_inBooleanNoRegisters 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:

EndpointWhat it does
GET /v1/agents/{slug}The agent's page: balance, spend, policy, identity, receipts and payments.
GET /v1/agents?mine=1Your agents, with pages turned off included.
PATCH /v1/agents/{slug}Changes the agent's settings.
POST /v1/agents/{slug}/keyIssues a new key and revokes the old one.

Change a policy

curl
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:

CodeStatusMeaning
slug_taken409Another agent has this slug.
slug_reserved400The slug is reserved.
too_many_agents409You have five agents already.
directory_full409The gateway's agent directory is full.
null400A 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
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
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.

RefusalStatusWhen
model_not_allowed403The model is not on the agent's or the key's list.
tool_not_allowed403The tool is not on the agent's list.
request_cap_exceeded402The request's worst case is above max_request_units.
spend_limit_exceeded402An 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
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
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_denied or approval_expired, all 403 and free.

The agent can also ask before it acts, with its own key. action is up to 200 characters:

Ask for approval first

curl
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.

FieldTypeRequiredNotes
toStringYesAn agent's slug, or an account id.
unitsString of unitsYesThe amount.
memoStringNoUp to 280 characters.

Pay another agent

curl
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.

CodeStatusMeaning
same_account400A payment to itself.
unknown_recipient404No agent or account with that id.
recipient_not_agent403Under your session, a payment must go to an agent.
idempotency_key_reused409The 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
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
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
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:

TermValue
Loading…

Market reads: GET /v1/launchpad/tokens, /{address}, /trades, /candles, and GET /v1/launchpad/stats. See Launchpad.

Next: SDKs and MCP