# API Reference

Base URL: `https://valiron-edge-proxy.onrender.com`

Endpoints are organized into three groups:

- **Trust evaluation** (`/operator/agent/*`, `/operator/gate/*`, etc.) — No authentication required. Used by the SDK to evaluate agents.
- **Dashboard** (`/operator/register`, `/operator/login`, `/operator/endpoints`, etc.) — JWT-authenticated. Used by the Operator Dashboard. See [DASHBOARD.md](./DASHBOARD.md) for the UI guide.
- **Agent-ready wrappers** (`/wrap/:operatorId/*`) — Public agent-facing URLs created from Dashboard endpoint configs. These can enforce x402/MPP payment, optional trust checks, rate limits, and upstream forwarding.

---

## Trust Evaluation Endpoints

### GET /operator/agent/:agentId

Retrieve the complete trust profile for an agent.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | ERC-8004 token ID |
| `chain` | query | string | no | Network override (default: ethereum) |

### Response — 200 OK

```json
{
  "agentId": "25459",
  "identity": {
    "agentId": "25459",
    "wallet": "0x52ce...",
    "tokenUri": "data:application/json;base64,...",
    "name": "Agent Name",
    "image": "https://...",
    "description": "Agent description",
    "endpoints": {
      "agentWallet": "eip155:1:0x...",
      "api": "https://api.example.com"
    },
    "trustModels": ["reputation_on_chain", "behavioral_sandbox"]
  },
  "onchainReputation": {
    "feedbackCount": 12,
    "averageScore": 92.5
  },
  "routing": {
    "finalRoute": "prod",
    "meetsThreshold": true
  },
  "chain": {
    "name": "ethereum",
    "chainId": 1
  },
  "timestamp": "2026-03-23T12:00:00.000Z"
}
```

### Error Responses

| Status | Meaning |
|--------|---------|
| 404 | Agent not found in ERC-8004 registry |
| 500 | Internal server error |

---

## GET /operator/wallet/:wallet

Reverse-lookup a wallet address to get its trust profile.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `wallet` | path | string | yes | Ethereum address (0x...) or Solana pubkey (base-58, with `chain=solana`) |
| `chain` | query | string | no | Network override |

### Response — 200 OK

Same structure as `GET /operator/agent/:agentId`.

---

## GET /operator/resolve-wallet/:wallet

Lightweight wallet → agentId resolution. Returns just the agent ID and how it was resolved, without the full trust profile.

Resolution chain: Redis → Agent0 subgraph → null. For Solana (`chain=solana`): QuantuLabs `getAgentsByOwner()`.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `wallet` | path | string | yes | Ethereum address (0x...) or Solana pubkey (base-58, with `chain=solana`) |
| `chain` | query | string | no | Network override (default: ethereum) |

### Response — 200 OK

```json
{
  "wallet": "0x52ce...",
  "agentId": "25459",
  "source": "subgraph",
  "chainId": 1,
  "agentName": "MyAgent",
  "timestamp": "2024-01-01T00:00:00.000Z"
}
```

---

## POST /operator/trigger-sandbox/:agentId

Execute real-time sandbox tests against an agent and compute a trust evaluation.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | ERC-8004 token ID |
| `chain` | query | string | no | Network override |

### Response — 200 OK

```json
{
  "ok": true,
  "agentId": "25459",
  "wallet": "0x52ce...",
  "tier": "AAA",
  "riskLevel": "GREEN",
  "meetsThreshold": true,
  "testSummary": {},
  "chain": {
    "name": "ethereum",
    "chainId": 1
  },
  "message": "Sandbox evaluation complete"
}
```

> **Note:** The `testSummary` object contains opaque test metrics. Its internal structure is proprietary and may change without notice.

---

## POST /operator/gate/:agentId

Trust gate — combined on-chain reputation + sandbox check. Returns an allow/deny decision. Designed for protecting payment endpoints (x402, etc.).

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | ERC-8004 token ID |

### Request Body

```json
{
  "ttlMs": 86400000
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `ttlMs` | number | 86400000 | Cache TTL in milliseconds (default 24 hours) |

### Response — 200 OK

```json
{
  "allow": true,
  "tier": "AAA",
  "riskLevel": "GREEN",
  "meetsThreshold": true,
  "route": "prod",
  "agentId": "25459",
  "wallet": "0x52ce...",
  "chain": {
    "name": "ethereum",
    "chainId": 1
  },
  "sandboxRan": false,
  "cached": true
}
```

### Decision Logic

1. If the agent meets the trust threshold → `allow: true`
2. If the agent does not meet the threshold → `allow: false`
3. Cached sandbox results are reused within the `ttlMs` window
4. If no cached result exists, sandbox tests run automatically

---

## GET /operator/agent/:agentId/snapshot

Get the latest behavioral snapshot hash for an agent. Returns an opaque hash suitable for on-chain commitment systems.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | ERC-8004 token ID |
| `chain` | query | string | no | Network override |

### Response — 200 OK

```json
{
  "agentId": "25459",
  "snapshotHash": "0x3a7f2b...",
  "previousHash": "0x0",
  "encryptedDataUri": null,
  "timestamp": "2026-04-01T12:00:00.000Z",
  "interactionCount": 15
}
```

### Error Responses

| Status | Meaning |
|--------|---------|
| 400 | Invalid agentId |
| 404 | Agent not found or no snapshot available (trigger a sandbox evaluation first) |
| 500 | Internal server error |

---

## POST /operator/webhooks/register

Register a webhook URL to receive `evaluation_complete` events whenever Valiron evaluates an agent.

### Request Body

```json
{
  "event": "evaluation_complete",
  "url": "https://your-endpoint.com/hooks/valiron",
  "agentIds": [42, 43]
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `event` | string | yes | Must be `"evaluation_complete"` |
| `url` | string | yes | HTTPS endpoint to receive webhook POST requests |
| `agentIds` | number[] | no | Filter to specific agent IDs. Omit for all agents. |

### Response — 201 Created

```json
{
  "id": "a1b2c3d4-...",
  "event": "evaluation_complete",
  "url": "https://your-endpoint.com/hooks/valiron",
  "agentIds": [42, 43],
  "createdAt": "2026-04-01T12:00:00.000Z"
}
```

### Webhook Payload

When an evaluation completes, Valiron POSTs the following to your registered URL:

```json
{
  "event": "evaluation_complete",
  "agentId": "25459",
  "tier": "AAA",
  "riskLevel": "GREEN",
  "route": "prod",
  "meetsThreshold": true,
  "snapshotHash": "0x3a7f2b...",
  "previousHash": "0x8b2c1a...",
  "encryptedDataUri": null,
  "timestamp": "2026-04-01T12:05:00.000Z",
  "interactionCount": 16
}
```

#### Webhook Payload Fields

| Field | Type | Description |
|-------|------|-------------|
| `event` | string | Always `"evaluation_complete"` |
| `agentId` | string | The evaluated agent ID |
| `tier` | string | Moody's-style trust tier (AAA–C) |
| `riskLevel` | string | GREEN / YELLOW / RED |
| `route` | string | Route decision (`prod`, `prod_throttled`, `sandbox`, `sandbox_only`) |
| `meetsThreshold` | boolean | Whether the agent passed the trust gate |
| `snapshotHash` | string | Deterministic hash of the behavioral evaluation |
| `previousHash` | string | Hash from the prior evaluation |
| `encryptedDataUri` | string\|null | IPFS CID of encrypted snapshot data |
| `timestamp` | string | ISO 8601 timestamp |
| `interactionCount` | number | Total interactions in the evaluation window |

#### Webhook Limitations

- **Maximum registrations:** 1,000 per operator
- **Delivery:** Fire-and-forget — no retries on failure
- **Timeout:** 10 seconds — your endpoint must respond within 10s
- **Signatures:** Webhooks are not signed — verify via IP allowlisting or shared secrets if needed
- **Events:** Only `evaluation_complete` is currently supported

---

## POST /operator/proxy/:agentId (Pro)

Forward a request through Valiron's edge proxy with automatic trust gating, logging, and SSRF protection.

### Parameters

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | Agent to gate |

### Request Body

```json
{
  "targetUrl": "https://your-api.com/data",
  "method": "POST",
  "headers": { "Content-Type": "application/json" },
  "body": "{\"query\": \"example\"}"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `targetUrl` | string | yes | Your origin URL to forward to |
| `method` | string | no | HTTP method (default: `GET`) |
| `headers` | object | no | Headers to forward |
| `body` | string | no | Request body |

### Response — 200 OK

```json
{
  "statusCode": 200,
  "responseHeaders": {},
  "responseBody": "...",
  "responseMs": 145,
  "costCharged": "0.0500",
  "route": "prod",
  "tierResolved": "AA"
}
```

> **SSRF Protection:** The proxy blocks requests to private IPs (10.x, 172.16-31.x, 192.168.x), localhost, link-local addresses, and cloud metadata endpoints (169.254.169.254).

---

## Key-Based Agent Endpoints

For Web2 agents that don't have on-chain identities, Valiron supports key-based authentication using EIP-191 challenge-response.

### POST /operator/key/:agentAddress/challenge

Get a challenge nonce for key-based authentication.

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentAddress` | path | string | yes | Ethereum address (0x...) |

#### Response — 200 OK

```json
{
  "challenge": "valiron-challenge-abc123...",
  "expiresAt": "2026-04-01T12:05:00.000Z",
  "agentAddress": "0x..."
}
```

### POST /operator/key/verify

Verify a signed challenge to authenticate a key-based agent.

#### Request Body

```json
{
  "agentAddress": "0x...",
  "challenge": "valiron-challenge-abc123...",
  "signature": "0x..."
}
```

#### Response — 200 OK

```json
{
  "verified": true,
  "agentAddress": "0x...",
  "score": null,
  "tier": null,
  "riskLevel": null,
  "route": null,
  "icebreaker": null,
  "reasons": [
    "Identity verified via cryptographic challenge-response",
    "No behavioral data yet — agent has not been scored"
  ],
  "timestamp": "2026-04-01T12:00:00.000Z"
}
```

### GET /operator/key/:agentAddress

Get the profile of a key-based agent.

#### Response — 200 OK

```json
{
  "agentAddress": "0x...",
  "verified": true,
  "score": 82,
  "tier": "A",
  "riskLevel": "GREEN",
  "route": "prod",
  "icebreaker": null,
  "reasons": [
    "Identity verified via cryptographic challenge-response",
    "Valiron behavioral tier: A (GREEN)"
  ],
  "timestamp": "2026-04-01T12:00:00.000Z"
}
```

---

## World ID Endpoints

Verify agent-human linkage via Worldcoin's zero-knowledge proof system.

### POST /operator/world-id/verify/:agentId

Submit a World ID proof for verification.

| Parameter | Location | Type | Required | Description |
|-----------|----------|------|----------|-------------|
| `agentId` | path | string | yes | ERC-8004 token ID |

#### Request Body

```json
{
  "merkle_root": "0x...",
  "nullifier_hash": "0x...",
  "proof": "0x...",
  "signal": "0x..."
}
```

#### Response — 200 OK

```json
{
  "verified": true,
  "agentId": "25459",
  "level": "orb",
  "verifiedAt": "2026-04-01T12:00:00.000Z"
}
```

### GET /operator/world-id/status/:agentId

Check if an agent has a verified World ID.

#### Response — 200 OK

```json
{
  "verified": true,
  "verifiedAt": "2026-04-01T12:00:00.000Z",
  "level": "orb"
}
```

### GET /operator/world-id/profile/:agentId

Get full World ID profile including verification details.

#### Response — 200 OK

```json
{
  "agentId": "25459",
  "worldId": {
    "verified": true,
    "level": "orb",
    "verifiedAt": "2026-04-01T12:00:00.000Z"
  }
}
```

---

## GET /operator/health

Health check endpoint.

### Response — 200 OK

```json
{
  "status": "ok",
  "supportedChains": ["ethereum", "monad", "arbitrum", "base", ...],
  "defaultChain": "ethereum"
}
```

---

## Solana Support

All `/operator/*` endpoints support Solana agents when `chain=solana` is passed as a query parameter or `x-chain: solana` header.

Solana agent identity and reputation data is provided by [QuantuLabs 8004-solana](https://8004.qnt.sh/), while EVM chains use the 8004 team's registry contracts. The two registries have different schemas — this is by design.

### Key Differences from EVM

| Area | EVM | Solana |
|------|-----|--------|
| **Agent ID format** | Numeric token ID (e.g. `25459`) | Base-58 Metaplex Core asset pubkey (e.g. `GL4k...`) or sequential ID (e.g. `1`, `42`) |
| **Wallet format** | `0x`-prefixed hex address | Base-58 public key |
| **Identity source** | ERC-8004 `ownerOf()` on-chain call | QuantuLabs `loadAgent()` SDK call |
| **Reputation source** | ERC-8004 `getSummary()` | QuantuLabs ATOM engine (`getEnrichedSummary()`) |
| **Extra reputation fields** | — | `solana.trustTier`, `solana.qualityScore`, `solana.confidence`, `solana.riskScore`, `solana.uniqueCallers` |
| **Wallet → Agent resolution** | Redis → Agent0 subgraph | QuantuLabs `getAgentsByOwner()` |
| **Liveness check** | — | `isItAlive()` endpoint-by-endpoint health probe |
| **Feedback write-back** | — | Valiron scores written on-chain via `giveFeedback()` (requires `SOLANA_FEEDBACK_KEYPAIR`) |

### Sequential Agent IDs

Solana agents are primarily identified by their base-58 asset pubkey, but the QuantuLabs indexer also assigns sequential integer IDs (1, 2, 3, …). You can use either format:

```bash
# By asset pubkey (primary)
curl "https://valiron-edge-proxy.onrender.com/operator/agent/GL4k...?chain=solana"

# By sequential ID (convenience)
curl "https://valiron-edge-proxy.onrender.com/operator/agent/42?chain=solana"
```

When a sequential ID is used, the response includes a `sequentialId` field mapping back to the numeric ID.

### Liveness Check

`GET /operator/agent/:agentId/liveness?chain=solana`

Pings each service endpoint from the agent's on-chain metadata and returns a health report. Only available for Solana agents.

**Response:**
```json
{
  "agentId": "GL4k...",
  "chain": { "network": "solana", "cluster": "mainnet-beta" },
  "liveness": {
    "status": "live",
    "okCount": 3,
    "totalPinged": 3,
    "skippedCount": 0,
    "results": [
      { "type": "rest", "endpoint": "https://...", "ok": true, "latencyMs": 120 }
    ]
  }
}
```

The `trigger-sandbox` endpoint also includes a `liveness` summary in its response for Solana agents.

### Feedback Write-Back

When `SOLANA_FEEDBACK_KEYPAIR` is configured, Valiron automatically writes evaluation scores back to the Solana reputation registry after sandbox testing and gate evaluations. This closes the trust loop — other consumers of the 8004-solana registry can see Valiron's scores.

Feedback is submitted as fire-and-forget using the `starred` tag with `tag2: "valiron"` for attribution.

### Solana-Specific Response Fields

When querying a Solana agent, the `onchainReputation` object may include an additional `solana` block with ATOM engine metrics:

```json
{
  "onchainReputation": {
    "count": "42",
    "averageScore": 88.5,
    "feedbackEntries": [],
    "totalFeedback": 42,
    "solana": {
      "trustTier": "trusted",
      "qualityScore": 92.1,
      "confidence": 0.87,
      "riskScore": 0.08,
      "uniqueCallers": 15
    }
  }
}
```

### Example Requests

```bash
# Get Solana agent profile
curl "https://valiron-edge-proxy.onrender.com/operator/agent/GL4k...?chain=solana"

# Get Solana agent by sequential ID
curl "https://valiron-edge-proxy.onrender.com/operator/agent/42?chain=solana"

# Gate a Solana agent
curl -X POST "https://valiron-edge-proxy.onrender.com/operator/gate/GL4k...?chain=solana"

# Check liveness
curl "https://valiron-edge-proxy.onrender.com/operator/agent/GL4k.../liveness?chain=solana"

# Look up by Solana wallet
curl "https://valiron-edge-proxy.onrender.com/operator/wallet/7xKX...?chain=solana"

# Trigger sandbox for Solana agent
curl -X POST "https://valiron-edge-proxy.onrender.com/operator/trigger-sandbox/GL4k...?chain=solana"
```

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `SOLANA_CLUSTER` | `mainnet-beta` | Solana cluster (`mainnet-beta` or `devnet`) |
| `SOLANA_RPC_URL` | Public RPC | Custom Solana RPC endpoint |
| `SOLANA_FEEDBACK_KEYPAIR` | — | JSON array secret key (e.g. `[1,2,...,64]`) for feedback write-back. When set, Valiron scores are written on-chain after evaluation. |

---

## Dashboard Endpoints (JWT-Authenticated)

All dashboard endpoints require a `Authorization: Bearer <jwt>` header. Get a JWT by calling `POST /operator/login`.

---

### POST /operator/register

Create a new operator account.

#### Request Body

```json
{
  "email": "you@example.com",
  "password": "SecurePass123!",
  "name": "Your Name"
}
```

#### Response — 200 OK

```json
{
  "operator": { "id": "uuid", "email": "you@example.com", "name": "Your Name", "role": "operator" },
  "apiKey": "val_op_xxxx..."
}
```

> **Important:** The `apiKey` is shown once. Copy it immediately.

---

### POST /operator/login

Authenticate and receive a JWT token.

#### Request Body

```json
{
  "email": "you@example.com",
  "password": "SecurePass123!"
}
```

#### Response — 200 OK

```json
{
  "operator": { "id": "uuid", "email": "you@example.com", "name": "Your Name", "role": "operator" },
  "token": "eyJhbGciOi..."
}
```

---

### GET /operator/me

Get the current operator's profile. Requires JWT.

#### Response — 200 OK

```json
{
  "ok": true,
  "operator": {
    "id": "uuid",
    "email": "you@example.com",
    "name": "Your Name",
    "role": "operator",
    "plan": "standard",
    "fee_rate": "0.03",
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  }
}
```

---

### PATCH /operator/me

Update operator name. Requires JWT.

#### Request Body

```json
{
  "name": "New Name"
}
```

---

### GET /operator/keys

List all API keys for the current operator. Requires JWT.

#### Response — 200 OK

```json
[
  {
    "id": "uuid",
    "keyPrefix": "val_op_c010...",
    "label": null,
    "revoked": false,
    "lastUsed": "2026-04-01T00:00:00.000Z",
    "createdAt": "2026-01-01T00:00:00.000Z"
  }
]
```

---

### POST /operator/keys/rotate

Generate a new API key. Requires JWT.

#### Request Body (optional)

```json
{
  "label": "production-key"
}
```

#### Response — 200 OK

```json
{
  "key": "val_op_xxxx...",
  "id": "uuid",
  "label": "production-key"
}
```

> **Important:** The full key is shown once. Copy it immediately.

---

### DELETE /operator/keys/:keyId

Revoke an API key. Requires JWT.

---

### GET /operator/endpoints

List all registered agent-ready wrappers for the current operator. Requires JWT.

#### Response — 200 OK

```json
[
  {
    "id": "uuid",
    "path": "/weather",
    "method": "GET",
    "pricePerCall": "0.001000",
    "description": "Returns weather data",
    "rateLimit": 60,
    "targetUrl": "https://api.example.com",
    "paymentProtocol": "x402",
    "paymentNetwork": "eip155:8453",
    "trustEnabled": false,
    "upstreamAuthType": "none",
    "active": true,
    "createdAt": "2026-01-01T00:00:00.000Z"
  }
]
```

---

### POST /operator/endpoints

Create a hosted agent-ready API wrapper. Requires JWT.

#### Request Body

```json
{
  "path": "/weather",
  "method": "GET",
  "pricePerCall": 0.001,
  "description": "Returns weather data",
  "rateLimit": 60,
  "targetUrl": "https://api.example.com",
  "paymentProtocol": "x402",
  "paymentNetwork": "eip155:8453",
  "payoutAddress": "0xYourReceivingWallet",
  "trustEnabled": false,
  "upstreamAuthType": "header",
  "upstreamAuthHeader": "Authorization",
  "upstreamAuthSecret": "Bearer upstream-secret"
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path` | string | yes | Public wrapper path (e.g. `/weather`). Callers use `/wrap/{operatorId}/weather`. |
| `method` | string | no | HTTP method (default: `GET`) |
| `pricePerCall` | number | yes | Price in USD per call. Use `0` for a free wrapper. |
| `description` | string | no | Endpoint description |
| `rateLimit` | number | no | Max requests/min per agent (null = unlimited) |
| `targetUrl` | string | yes | Existing upstream API base URL. Valiron appends `path` to this base URL when forwarding. Must be HTTPS in production. Private/internal addresses are blocked. |
| `paymentProtocol` | string | no | `x402` or `mpp`. Defaults to `x402`. |
| `paymentNetwork` | string | no | Payment network. Use `eip155:8453` or `eip155:84532` for x402, and `tempo:4217` or `tempo:42431` for MPP. |
| `payoutAddress` | string | no | Receiving wallet for paid x402 wrappers. Required if no platform default receiving address is configured. |
| `trustEnabled` | boolean | no | When true, the wrapper requires Valiron agent identity headers and checks trust before forwarding. |
| `upstreamAuthType` | string | no | `none` or `header`. Defaults to `none`. |
| `upstreamAuthHeader` | string | no | Header name to attach to the upstream request, such as `Authorization` or `X-API-Key`. |
| `upstreamAuthSecret` | string | no | Secret value for `upstreamAuthHeader`. Stored encrypted at rest. |

---

### DELETE /operator/endpoints/:endpointId

Deactivate an endpoint (soft delete). Requires JWT.

---

## Agent-Ready Wrapper Endpoint

### ALL /wrap/:operatorId/*

Public agent-facing wrapper URL. This endpoint is created by `POST /operator/endpoints` or the **Make API Agent-Ready** dashboard flow.

```text
https://valiron-edge-proxy.onrender.com/wrap/{operatorId}/{path}
```

The wrapper matches the operator ID, path, and HTTP method to an active endpoint config. It can enforce payment, optional trust checks, rate limits, SSRF protection, upstream auth injection, and request forwarding.

#### Caller Headers

| Header | Required | Description |
|--------|----------|-------------|
| `X-PAYMENT` | paid x402 retry | x402 payment payload returned by an x402-capable client. |
| `Authorization: Payment ...` | paid MPP retry | MPP payment authorization header. |
| `x-agent-id` | if trust enabled | ERC-8004 agent ID. |
| `x-agent-address` | if trust enabled | Key-based agent wallet address. |
| `x-agent-session` | key agents | Session token from key-based challenge-response. |

#### Free Wrapper Response

If `pricePerCall` is `0`, the wrapper forwards directly to the upstream API:

```bash
curl https://valiron-edge-proxy.onrender.com/wrap/{operatorId}/status
```

#### x402 Paid Wrapper Response

If `paymentProtocol` is `x402` and no valid payment is attached, the wrapper returns:

```http
HTTP/1.1 402 Payment Required
X-PAYMENT-RESPONSE: ...
Content-Type: application/json
```

The caller should retry with a valid `X-PAYMENT` header. See [AGENT-READY-APIS.md](./AGENT-READY-APIS.md#paid-x402-client) for a complete TypeScript client.

#### MPP Paid Wrapper Response

If `paymentProtocol` is `mpp` and no valid payment is attached, the wrapper returns:

```http
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment ...
Content-Type: application/json
```

The caller should retry with `Authorization: Payment ...`. For command-line testing:

```bash
pnpm exec mppx https://valiron-edge-proxy.onrender.com/wrap/{operatorId}/research
```

#### Error Responses

| Status | Code | Meaning |
|--------|------|---------|
| 402 | `payment_required` | Paid wrapper needs valid x402 or MPP payment. |
| 403 | `trust_denied` | Trust checks are enabled and the agent did not pass. |
| 404 | `wrapper_not_found` | No active wrapper matched operator ID, path, and method. |
| 413 | `payload_too_large` | Request body exceeds wrapper limit. |
| 429 | `rate_limit_exceeded` | Caller exceeded wrapper rate limit. |
| 502 | `wrapper_request_failed` | Upstream request failed or returned an invalid response. |

---

### GET /operator/analytics/stats

Aggregate stats for the current operator. Requires JWT.

#### Response — 200 OK

```json
{
  "totalCalls": 1250,
  "totalRevenue": "62.50",
  "uniqueAgents": 18,
  "avgResponseMs": 145,
  "errorRate": "0.024",
  "callsToday": 42,
  "revenueToday": "2.10"
}
```

---

### GET /operator/analytics/revenue?days=30

Daily revenue time series. Requires JWT.

#### Response — 200 OK

```json
[
  { "date": "2026-04-01", "revenue": "3.50", "calls": 70 },
  { "date": "2026-04-02", "revenue": "4.25", "calls": 85 }
]
```

---

### GET /operator/analytics/top-endpoints?limit=10

Top endpoints ranked by call volume. Requires JWT.

---

### GET /operator/analytics/agents?sortBy=lastSeen&limit=50&offset=0

Paginated agent summaries. Requires JWT.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `sortBy` | string | `lastSeen` | Sort by: `lastSeen`, `calls`, `spent`, `errors` |
| `limit` | number | 50 | Page size |
| `offset` | number | 0 | Offset |

---

### GET /operator/analytics/agents/:agentId

Detailed view of a single agent. Requires JWT. Returns summary stats, endpoint breakdown, and recent call log.

---

### GET /operator/analytics/logs

Paginated call logs. Requires JWT.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | number | 50 | Page size |
| `offset` | number | 0 | Offset |
| `days` | number | — | Filter to last N days |
| `agentId` | string | — | Filter by agent |
| `endpointId` | string | — | Filter by endpoint |

---

### POST /operator/playground

Execute a test request through the edge proxy as a simulated agent. Requires JWT. See [DASHBOARD.md — API Playground](./DASHBOARD.md#api-playground).

#### Request Body

```json
{
  "method": "GET",
  "path": "/api/v1/weather?city=NYC",
  "headers": { "Content-Type": "application/json" },
  "body": null,
  "agentId": "25459",
  "chain": "ethereum",
  "dryRun": false
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `path` | string | yes | Target path or absolute URL |
| `agentId` | string | yes | ERC-8004 agent ID to simulate |
| `method` | string | no | HTTP method (default: `GET`) |
| `headers` | object | no | Custom request headers |
| `body` | string | no | Request body (for POST/PUT/PATCH) |
| `chain` | string | no | Chain (default: `ethereum`) |
| `dryRun` | boolean | no | If `true`, returns trust evaluation without logging the call |

#### Response — 200 OK (standard mode)

```json
{
  "ok": true,
  "statusCode": 200,
  "statusText": "OK",
  "responseHeaders": {},
  "responseBody": "<downstream response body or trust evaluation JSON>",
  "responseMs": 145,
  "costCharged": "0.0500",
  "route": "prod",
  "tierResolved": "AA"
}
```

#### Response — 200 OK (dry-run mode)

```json
{
  "ok": true,
  "statusCode": null,
  "statusText": null,
  "responseHeaders": {},
  "responseBody": "{\"dryRun\":true,\"message\":\"Routing and pricing validated — request was not forwarded to your backend\",\"agentId\":\"25459\",\"chain\":\"ethereum\",\"tier\":\"AA\",\"route\":\"prod\",\"pricePerCall\":\"0.0500\",\"endpointRegistered\":true,\"trustProfile\":{\"onchainScore\":92.5,\"feedbackCount\":\"12\",\"routingDecision\":null}}",
  "responseMs": null,
  "costCharged": "0.0500",
  "route": "prod",
  "tierResolved": "AA"
}
```

---

### Admin Endpoints (require `role: "admin"`)

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/operator/admin/stats` | GET | Platform-wide stats |
| `/operator/admin/operators` | GET | List all operators with aggregate stats |
| `/operator/admin/operators/:id` | GET | Full operator detail (keys, endpoints, agents, revenue) |
| `/operator/admin/operators/:id/logs` | GET | Call logs for a specific operator |
