# Agents
URL: /docs/x1id/agents

> Give an AI agent an on-chain @handle identity — a validated manifest of its capabilities and endpoints, a chain-verified pay-to address for x402 payments, and an MCP endpoint a client can discover from the handle alone. Resolve it all through GET /v1/agents/:handle.

# Agents

<Pill tone="mainnet">Mainnet</Pill>

<LlmActions />

An **agent** is an ordinary `@handle` that advertises a machine-readable **manifest** — its capabilities, its endpoints (MCP, x402, A2A), and a pay-to address that's verified on-chain. A client holding only `@payday-bot` can discover how to talk to it, how to pay it, and what it can do. This is conventions and packaging on top of primitives X1ID already ships — **no new on-chain program**, no new account family.

## Resolve an agent

<Endpoint method="GET" path="/v1/agents/:handle" />

Resolve a handle, read its on-chain `agent.manifest` record, fetch the off-chain manifest, validate it against the chain, and return it. Keyed or [x402-metered](/docs/x1id/platform#pay-per-call-with-x402), priced exactly like a resolve (`@` in the path is optional).

```bash title="GET /v1/agents/:handle"
curl https://api.x1id.io/v1/agents/payday-bot \
  -H "X-API-Key: $X1ID_API_KEY"
```

```json
{
  "name": "payday-bot",
  "handle": "<base58 Handle PDA>",
  "handleType": 3,
  "agent": true,
  "manifestUrl": "https://payday.example/.well-known/x1id-agent.json",
  "endpoints": {
    "mcp": "https://mcp.example.com",
    "x402": "https://api.example.com/pay"
  },
  "manifest": { "x1id": { "version": 1, "name": "payday-bot", "network": "mainnet", "handle": "<PDA>" }, "...": "…" }
}
```

| field         | meaning                                                                                                                                                                              |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `handle`      | the Handle PDA (base58) — the uniqueness anchor.                                                                                                                                     |
| `handleType`  | the on-chain `Handle.handle_type`; `3` is the strong, immutable `Agent` flag. Require `handleType === 3` if you want only the strong signal.                                         |
| `manifestUrl` | the `agent.manifest` record value the document was fetched from (`https://` or `ipfs://`).                                                                                           |
| `endpoints`   | the agent's well-known endpoint records read straight off the handle — `mcp`, `x402`, `a2a`, `capabilities` — so you find one without parsing the manifest. Absent keys are omitted. |
| `manifest`    | the fetched document, validated for shape **and** agreement with the chain.                                                                                                          |

A handle that isn't an agent returns `404 NOT_AN_AGENT`; an agent with no manifest record `404 NO_MANIFEST`; a manifest that's unreachable `424 MANIFEST_UNREACHABLE`; one whose contents don't match the handle `422 INVALID_MANIFEST`. A `424` is a problem with the agent owner's off-chain manifest URL, not with the gateway — its message names the specific cause (host did not resolve, connection refused, timed out, TLS certificate could not be verified). The gateway already retries a transient fetch blip once internally, so a brief hiccup never surfaces as a `424`. **If you paid for the call with x402, a `424` does not consume your payment** — only a successful `200` settles it. You can retry the **same** payment within the 600-second redeem window, counted from the payment's on-chain block time (the quote's `maxTimeoutSeconds` stays 60 s). If the manifest is still unreachable after that, send a **new** payment.

### What makes a handle an agent

Either signal counts:

1. **`HandleType::Agent`** (`handle_type == 3`) — set at registration, immutable, tamper-proof. The strong flag.
2. **An `agent.manifest` text record exists** — a soft flag so a handle registered as a person/org can *also* operate an agent without losing its name (since `handle_type` is immutable).

## The manifest

The manifest is a JSON document hosted off-chain (it's larger than a 256-byte text record and changes often). The chain stores the **pointer** (`agent.manifest` → URL) and the **payment facts** (address records); the document itself is fetched and then checked against the chain.

```jsonc title="x1id agent manifest (v1)"
{
  "x1id": {
    "version": 1,
    "name": "payday-bot",              // the handle this manifest is FOR
    "network": "mainnet",              // mainnet | testnet
    "handle": "<base58 Handle PDA>"    // the uniqueness anchor
  },
  "identity":   { "attestation": "<base58 Attestation PDA>", "description": "…" },
  "endpoints":  [ { "type": "mcp",  "url": "https://mcp.example.com" },
                  { "type": "x402", "url": "https://api.example.com/pay" } ],
  "capabilities": { "schema": "https://…", "url": "https://…" },
  "payment": {
    "addresses": [ { "coinType": 501, "address": "…", "verified": true } ],
    "x402":      { "network": "solana:4SvBP3omtvcCVWdxq1zBY5cDp4wndjsT", "asset": "…", "scheme": "exact", "facilitator": "https://…" }
  }
}
```

The `payment.x402.network` is a **CAIP-2** chain id, not a friendly label: on X1 **mainnet** it is `solana:4SvBP3omtvcCVWdxq1zBY5cDp4wndjsT` (the `solana:<genesis-hash>` form), which is exactly the `network` an x402 `402` challenge from `api.x1id.io/v1` advertises — match it byte-for-byte rather than hardcoding a name.

<Callout type="warn">
  **The chain wins.** A manifest self-identifies the exact handle it's for (`x1id.name` + `x1id.handle`), and a manifest served for the wrong name, handle, or network is **discarded**, never partially trusted — so a hosting compromise on one agent's server can't re-point a different agent's manifest at its own endpoints. A `verified: true` payment address the chain doesn't actually verify is rejected: the pay-to source of truth is the handle's on-chain, signature-verified address record, never the manifest's self-report.
</Callout>

### Validate a manifest before you host it

<Endpoint method="POST" path="/v1/agents/validate" />

An authoring aid: check a candidate manifest against a handle's derived identity **before** you host it and point the record at it. Pure (no RPC), so it works even before the handle is registered.

```bash title="POST /v1/agents/validate"
curl -X POST https://api.x1id.io/v1/agents/validate \
  -H "X-API-Key: $X1ID_API_KEY" -H "content-type: application/json" \
  -d '{ "handle": "@payday-bot", "manifest": { "x1id": { "version": 1, "...": "…" } } }'
# → { "name": "payday-bot", "handle": "<PDA>", "network": "mainnet",
#     "valid": true, "errors": [],
#     "note": "shape + identity only — the on-chain verified-address cross-check runs at read time (GET /v1/agents/:handle)" }
```

## x402 agent payments

Agents charge per call through the **same x402 rail** X1ID's own API uses: an unpaid request to the agent's endpoint gets `402 Payment Required` with an `accepts[]` body; the caller pays and retries with a `PAYMENT-SIGNATURE` header (v1 name `X-PAYMENT`); the endpoint settles and serves. On X1ID's own API the retry must also carry the payer's signature over the challenge's `redeemMessage` ([details](/docs/x1id/platform#sign-the-redemption-with-the-paying-wallet)). The preferred `payTo` is the handle's **chain-verified** payment address — proved, not asserted — which is the strongest pay-to guarantee in the space. See [Getting paid (x402)](/docs/x1id/get-paid#agents-can-pay-too-x402).

## MCP discovery

A client holding only `@payday-bot` finds its MCP endpoint two ways, cheapest first:

1. **The `agent.mcp` record** — one lookup, no document fetch. Read it directly, or take `endpoints.mcp` straight from the `GET /v1/agents/:handle` response.
2. **The manifest `endpoints[]`** — richer, with every endpoint and its `type`. The record and the manifest should agree; the record is the low-latency shortcut, the manifest the complete picture.

```
@payday-bot
  → GET /v1/agents/payday-bot
      → endpoints.mcp         (direct, from the agent.mcp record)   ← MCP URL
      → manifest.endpoints[]  (type == "mcp")                       ← confirms / enriches
  → open an MCP session against that URL
      → paid tools speak x402, payTo = the handle's verified address
```

See the [MCP server](/docs/x1id/mcp) page for X1ID's own MCP surface and the agent-verification ladder (L0 declared → L1 attested → L2 pay-to confirmed).

## Next

<Cards>
  <Card href="/docs/x1id/mcp" icon="Bot" title="MCP server" description="The x1id MCP tools and the agent-verification levels." />

  <Card href="/docs/x1id/get-paid" icon="QrCode" title="Getting paid (x402)" description="The x402 pay-per-call flow agents charge through." />

  <Card href="/docs/x1id/records" icon="Link" title="Address records" description="The on-chain, signature-verified pay-to an agent's payments resolve to." />
</Cards>
