Agents API
The Agents API issues and manages AI-agent identity: did:solidus DIDs scoped to agents (never their operator), BBS+ credentials, ERC-8004 on-chain passports, and spend mandates — a scoped, verifiable spend authorization a counterparty can check without calling back to Solidus.
Base URL: https://agents.solidus.network/v1
This is the surface @solidus/mcp and the agent SDKs consume directly.
The full spec
Every route on this page is generated from the same zod schemas the server validates requests against — the spec cannot drift from what the server actually does, because it isn’t hand-maintained. This page is a curated tour, not the exhaustive reference; for the complete, current request/response shape of every route, use:
- Interactive reference:
https://agents.solidus.network/documentation - Raw OpenAPI 3.1 document:
https://agents.solidus.network/openapi.json
Authentication
Operator-gated routes accept a single operatorBearer scheme with two interchangeable credential shapes, both sent as Authorization: Bearer <token>:
- Operator JWT — minted by
POST /v1/operators, expires per the server’s configured TTL. - API key — a revocable
sk_...key fromPOST /v1/api-keys, for long-lived programmatic/integrator use.
curl https://agents.solidus.network/v1/agents \
-H "Authorization: Bearer sk_..."A handful of routes are public by design — no auth, because the proof of authorization travels in the request itself:
POST /v1/mandates/check— the mandate-check flow belowPOST /v1/credentials/verify— live credential verificationGET /v1/agents/{agentId}/card,GET /v1/credentials/{credentialId}/descriptor,GET /v1/status-lists/{listId}— resolvable public documents
Key flow: register an operator, create an agent
# 1. Register an operator — returns a bearer token
curl -X POST https://agents.solidus.network/v1/operators \
-H "content-type: application/json" \
-d '{"kyc": {"credentialRef": "urn:solidus:credential:kyc-abc", "assuranceLevel": "substantial"}}'// Response 201
{
"operator": { "id": "...", "tenantId": "...", "kycAssuranceLevel": "substantial", "createdAt": "..." },
"token": "eyJhbGciOiJFZERTQSIs..."
}# 2. Create an agent under that operator — gets its own did:solidus DID
curl -X POST https://agents.solidus.network/v1/agents \
-H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \
-H "content-type: application/json" \
-d '{"custody": "managed"}'// Response 201
{
"agent": {
"id": "...",
"operatorId": "...",
"did": "did:solidus:testnet:...",
"custody": "managed",
"status": "active",
"capabilities": [],
"passport": null,
"createdAt": "..."
}
}Key flow: the mandate-check — the wedge feature
A spend mandate is a BBS+ credential that authorizes an agent to spend up to a limit, in a currency, on a rail, until an expiry — issued once via POST /v1/agents/{agentId}/mandates, then checked by any counterparty without ever calling back to the issuing operator. The proof travels as an X-Solidus-Mandate envelope header; the check is public, unauthenticated, and fails closed at every step (unknown mandate, expired, revoked, over-limit, wrong currency, wrong rail).
This is exactly what @solidus/mcp’s check_mandate tool calls:
curl -X POST https://agents.solidus.network/v1/mandates/check \
-H "content-type: application/json" \
-d '{
"envelope": "<the X-Solidus-Mandate header value>",
"amount": "12.50",
"currency": "USDC",
"rail": "x402"
}'// Response 200 — authorized
{
"authorized": true,
"agentDid": "did:solidus:testnet:...",
"principalDid": "did:solidus:testnet:...",
"maxAmount": "50",
"expiresAt": "2026-08-01T00:00:00.000Z",
"verifiedAsOf": "2026-07-22T01:00:00.000Z"
}// Response 200 — denied (any reason: unknown mandate, expired, revoked, over-limit, wrong currency/rail, invalid proof)
{
"authorized": false,
"reason": "mandate revoked"
}authorized/reason are passed through verbatim by @solidus/mcp — this is the exact refusal contract that makes the whole product trustworthy, and it does not change without a corresponding SDK release.
Route index
| Route group | Base path | Auth |
|---|---|---|
| Operators | /v1/operators | Public register, operatorBearer for reads/updates |
| Agents | /v1/agents | operatorBearer (except the public agent card) |
| Credentials | /v1/agents/{agentId}/credentials | operatorBearer (issue/list/revoke); public verify + descriptor live under /v1/credentials |
| Status lists | /v1/status-lists/{listId} | Public (draft-ietf-oauth-status-list JWT) |
| Mandates | /v1/agents/{agentId}/mandates, /v1/mandates/check | operatorBearer for lifecycle; the check itself is public |
| Billing | /v1/billing/usage | operatorBearer |
| Webhooks | /v1/webhooks | operatorBearer |
| API keys | /v1/api-keys | operatorBearer |
| ERC-8004 | /v1/erc8004 | operatorBearer |
For each route’s exact request body, response shape, and status codes, see https://agents.solidus.network/documentation.
Error format
Errors follow RFC 7807 Problem Details , the same shape across every route:
{
"type": "https://solidus.network/errors/validation",
"title": "Validation Error",
"status": 400,
"detail": "envelope: Required",
"instance": "/v1/mandates/check"
}What generated clients cover
A generated client (Go, Python, and others — in progress) wraps this HTTP surface only. It will not replace @solidus/agent-identity’s BBS+ selective disclosure, key handling, or credential-bundle helpers — those are libraries, not HTTP wrappers. Use the TypeScript SDK packages for that; use a generated client (or this spec directly) for calling the API from another language.