Agent-native accounts

An AI agent can open its own Bruce account, with no website, phone or human login, and use it the way a signed-in person does: set a profile, share pay details, request and collect money, and send payments. The wallet is owned by a key that only the agent holds.

Non-custodial At signup the agent generates a P-256 key pair and sends Bruce only the public key. Bruce creates a Polygon USDC wallet owned by that key. Every on-chain action needs a signature from the agent, and Bruce cannot move the funds.

If you want an agent to spend from your balance under caps instead, use delegated agent keys. Agent-native accounts are separate accounts that belong to the agent.

1. Sign up

  1. GET /api/agent-auth/pow returns { token, bits }. Find a nonce where sha256("<token>:<nonce>") starts with at least bits zero bits. At the default of 20 bits this takes about a second.
  2. POST /api/agent-auth/register with { publicKey, pow: { token, nonce }, displayName?, username? }. publicKey is the base64 SPKI of the P-256 key.

The response includes userId and walletAddress. Signups are rate-limited per IP, and each puzzle can be used only once.

2. Log in

  1. POST /api/agent-auth/challenge with { publicKey } returns { challengeId, message }. The challenge is valid for 5 minutes.
  2. Sign the UTF-8 message using ECDSA P-256 with SHA-256. Send the signature as base64 DER or raw r||s.
  3. POST /api/agent-auth/session with { challengeId, signature } returns { accessToken: "bas_…", expiresAt }.

Send Authorization: Bearer bas_… on every /api/* call. Sessions last one hour by default. To get a new one, log in again. POST /api/agent-auth/session/revoke ends a session.

3. Sign money movements

Every on-chain action uses the same three steps:

  1. Call the …/prepare endpoint. It returns payloadBase64 and requestExpiry.
  2. Base64-decode the payload and sign the bytes with the agent key (ECDSA P-256 / SHA-256, DER, low-S).
  3. Call the submit endpoint with the same body plus authorizationSignature and requestExpiry. Gas is sponsored.
ActionPrepareSubmit
Send USDC (handle or address)POST /api/transactions/send/preparePOST /api/transactions/send
Escrow actionsPOST /api/escrows/:id/relay/preparePOST /api/escrows/:id/relay (add autoConfirm: true)
Scheduled paymentsPOST /api/scheduled-payments/:id/relay/preparePOST /api/scheduled-payments/:id/relay (add autoConfirm: true)
Batch payouts (per chunk)POST /api/batch-payments/:id/preparePOST /api/batch-payments/:id/confirm with itemIds
Approved org sendPOST /api/organizations/:id/approvals/:approvalId/preparePOST …/execute

If an agent submits without a signature, it gets 400 with code: "signature_required" and a hint that names the prepare endpoint.

With autoConfirm, Bruce records the state change (for example a confirmed deposit or a release) in the same call. The response includes it under confirm.

What agents can do

Everything on the logged-in API works the same way for agents, including:

Paying a request or a link is a signed send with paymentRequestId or paymentLinkId, followed by PATCH …/pay with the txHash.

Verified payments and withdrawals still need KYC or KYB, the same as for people.

These endpoints need a person and return 403 with not_available_for_agent_accounts:

Limits

Tools

You don't have to implement signing yourself. The TypeScript SDK and the local MCP server handle it for you.