{
	"openapi": "3.1.0",
	"info": {
		"title": "Agent Payments with Ledger API",
		"version": "0.2.0",
		"description": "Agent-first API for submitting transaction intents. AI agents propose transactions; humans review and sign on Ledger hardware. Agents never touch private keys.\n\n## Quick Start\n1. Register your agent key (human signs authorization on Ledger)\n2. `POST /api/intents` with AgentAuth header to propose a transaction\n3. Share the returned `paymentUrl` with the human\n4. Poll `GET /api/intents/{id}` until status is `confirmed`, `rejected`, `failed`, or `expired`\n\n## Authentication\nAgents authenticate via the `Authorization: AgentAuth <timestamp>.<bodyHash>.<signature>` header.\n- `timestamp`: Unix epoch seconds (within 5 min of server time)\n- `bodyHash`: keccak256 of raw JSON body as hex (use `0x` for GET)\n- `signature`: EIP-191 personal_sign of `<timestamp>.<bodyHash>` with agent's secp256k1 key"
	},
	"servers": [
		{ "url": "https://agent-intents-web.vercel.app", "description": "Production" },
		{ "url": "/", "description": "Same-origin" }
	],
	"tags": [
		{ "name": "Intents", "description": "Create, read, and update payment intents" },
		{ "name": "Polymarket", "description": "Search Polymarket markets and create trade intents" },
		{ "name": "Agents", "description": "Agent provisioning and management" },
		{ "name": "Auth", "description": "Session-based auth for web UI (EIP-712 challenge/verify)" },
		{ "name": "Health", "description": "Service health" }
	],
	"paths": {
		"/api/polymarket/markets": {
			"get": {
				"tags": ["Polymarket"],
				"operationId": "searchPolymarketMarkets",
				"summary": "Search active Polymarket markets",
				"description": "Search for active Polymarket prediction markets by keyword. Returns simplified market data including conditionId, question, prices, and volume. No authentication required — data is public.\n\nUse this endpoint to discover the `conditionId` needed to create a `polymarket_trade` intent.",
				"parameters": [
					{
						"name": "q",
						"in": "query",
						"required": false,
						"description": "Search keyword (e.g. 'bitcoin', 'trump', 'ethereum')",
						"schema": { "type": "string", "default": "" }
					},
					{
						"name": "limit",
						"in": "query",
						"required": false,
						"description": "Max results (1–50)",
						"schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 10 }
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/PolymarketMarketsResponse" },
								"examples": {
									"search_bitcoin": {
										"summary": "Search for Bitcoin markets",
										"value": {
											"success": true,
											"markets": [
												{
													"conditionId": "0xabc123...",
													"question": "Will Bitcoin hit $100k by July 2026?",
													"yesPrice": 0.65,
													"noPrice": 0.35,
													"volume": 1250000,
													"endDate": "2026-07-01T00:00:00.000Z",
													"active": true
												}
											]
										}
									}
								}
							}
						}
					},
					"502": {
						"description": "Upstream Gamma API error",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"503": {
						"description": "Service temporarily unavailable",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/health": {
			"get": {
				"tags": ["Health"],
				"operationId": "healthCheck",
				"summary": "Health check",
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/HealthResponse" }
							}
						}
					}
				}
			}
		},
		"/api/intents": {
			"get": {
				"tags": ["Intents"],
				"operationId": "listIntents",
				"summary": "List intents for a user",
				"description": "Returns intents for a given userId, optionally filtered by status. Requires session auth and supports cursor pagination.",
				"parameters": [
					{
						"name": "userId",
						"in": "query",
						"required": true,
						"description": "Wallet address (e.g., 0xabc...)",
						"schema": { "type": "string" }
					},
					{
						"name": "status",
						"in": "query",
						"required": false,
						"description": "Filter by intent status",
						"schema": { "$ref": "#/components/schemas/IntentStatus" }
					},
					{
						"name": "limit",
						"in": "query",
						"required": false,
						"description": "Max results (1–100)",
						"schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
					},
					{
						"name": "cursor",
						"in": "query",
						"required": false,
						"description": "Opaque cursor returned by the previous page",
						"schema": { "type": "string" }
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/IntentListResponse" }
							}
						}
					},
					"400": {
						"description": "Missing userId",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			},
			"post": {
				"tags": ["Intents"],
				"operationId": "createIntent",
				"summary": "Create a payment intent",
				"description": "Submit a new transaction intent for human review and Ledger signing. Requires AgentAuth. The userId is derived from the authenticated agent's trustchain — do not pass it in the body.\n\nReturns a `paymentUrl` that the agent should share with the human so they can review and sign.",
				"security": [{ "AgentAuth": [] }],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/CreateIntentRequest" },
							"examples": {
								"simple_transfer": {
									"summary": "Simple USDC transfer",
									"value": {
										"agentId": "my-research-bot",
										"agentName": "Research Assistant",
										"urgency": "normal",
										"expiresInMinutes": 60,
										"details": {
											"type": "transfer",
											"token": "USDC",
											"amount": "5.00",
											"recipient": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
											"chainId": 8453,
											"memo": "API access fee"
										}
									}
								},
								"x402_payment": {
									"summary": "x402 pay-per-call API payment",
									"value": {
										"agentId": "my-research-bot",
										"agentName": "Research Assistant",
										"details": {
											"type": "transfer",
											"token": "USDC",
											"amount": "0.01",
											"recipient": "0xPayToAddress...",
											"chainId": 8453,
											"memo": "x402 payment for CoinGecko Pro API",
											"resource": "https://api.coingecko.com/pro/v1/coins/bitcoin",
											"category": "api_payment",
											"x402": {
												"resource": { "url": "https://api.coingecko.com/pro/v1/coins/bitcoin" },
												"accepted": {
													"scheme": "exact",
													"network": "eip155:8453",
													"amount": "10000",
													"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
													"payTo": "0xPayToAddress..."
												}
											}
										}
									}
								},
								"polymarket_trade": {
									"summary": "Polymarket prediction market trade",
									"value": {
										"agentId": "my-research-bot",
										"agentName": "Research Assistant",
										"urgency": "normal",
										"expiresInMinutes": 60,
										"details": {
											"type": "polymarket_trade",
											"conditionId": "0xabc123...",
											"outcome": "Yes",
											"amount": "50",
											"chainId": 137,
											"memo": "Based on current market analysis, this outcome is likely"
										}
									}
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Intent created",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/CreateIntentResponse" }
							}
						}
					},
					"400": {
						"description": "Validation error",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Missing or invalid AgentAuth header",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"429": {
						"description": "Rate limit exceeded (max 10 intents per agent per minute)",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"503": {
						"description": "Service temporarily unavailable",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/intents/{id}": {
			"get": {
				"tags": ["Intents"],
				"operationId": "getIntent",
				"summary": "Get intent by ID",
				"description": "Returns intent details. Without auth, x402 secrets (paymentSignatureHeader, paymentPayload) are stripped. With AgentAuth from the owning agent, full x402 secrets are included.",
				"security": [{}, { "AgentAuth": [] }],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"description": "Intent ID (e.g., int_1707048000_abc12345)",
						"schema": { "type": "string" }
					}
				],
				"responses": {
					"200": {
						"description": "OK — intent found. x402 secrets included only if caller is the owning agent.",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/IntentResponse" }
							}
						}
					},
					"400": {
						"description": "Missing intent ID",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Intent not found",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/intents/status": {
			"post": {
				"tags": ["Intents"],
				"operationId": "updateIntentStatus",
				"summary": "Update intent status",
				"description": "Update the status of an intent. Agents (AgentAuth) can set: `executing`, `confirmed`, `failed`. Humans (session cookie) can set: `approved`, `rejected`, `authorized`, `broadcasting`. The caller must own the intent.\n\nThis is the preferred route (static path avoids Vercel dynamic-route issues).",
				"security": [{ "AgentAuth": [] }, { "SessionCookie": [] }],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/UpdateIntentStatusByIdRequest" },
							"examples": {
								"agent_confirms": {
									"summary": "Agent confirms x402 settlement",
									"value": {
										"id": "int_1707048000_abc12345",
										"status": "confirmed",
										"note": "x402 payment settled successfully",
										"settlementReceipt": {
											"txHash": "0xabc123...",
											"network": "eip155:8453",
											"success": true
										}
									}
								},
								"agent_reports_failure": {
									"summary": "Agent reports failure",
									"value": {
										"id": "int_1707048000_abc12345",
										"status": "failed",
										"note": "x402 server returned 500 after payment"
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Status updated",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/IntentResponse" } }
						}
					},
					"400": {
						"description": "Invalid status or missing fields",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication failed",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Not authorized (wrong owner or disallowed status transition)",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Intent not found",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"409": {
						"description": "Invalid state transition (e.g., confirmed → pending)",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/intents/{id}/status": {
			"patch": {
				"tags": ["Intents"],
				"operationId": "updateIntentStatusLegacy",
				"summary": "Update intent status (legacy)",
				"description": "Legacy alternative to POST /api/intents/status. Same authentication and permission rules apply. Prefer the POST route.",
				"security": [{ "AgentAuth": [] }, { "SessionCookie": [] }],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": { "type": "string" }
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/UpdateIntentStatusRequest" }
						}
					}
				},
				"responses": {
					"200": {
						"description": "Status updated",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/IntentResponse" } }
						}
					},
					"400": {
						"description": "Bad request",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication failed",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Not authorized",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Not found",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"409": {
						"description": "Invalid state transition",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/users/{userId}/intents": {
			"get": {
				"tags": ["Intents"],
				"operationId": "listUserIntents",
				"summary": "List intents for a user (path param)",
				"description": "Same as GET /api/intents?userId=... but with userId in the path. Requires session cookie; callers can only list their own intents. Supports cursor pagination.",
				"security": [{ "SessionCookie": [] }],
				"parameters": [
					{
						"name": "userId",
						"in": "path",
						"required": true,
						"description": "Wallet address",
						"schema": { "type": "string" }
					},
					{
						"name": "status",
						"in": "query",
						"required": false,
						"schema": { "$ref": "#/components/schemas/IntentStatus" }
					},
					{
						"name": "limit",
						"in": "query",
						"required": false,
						"schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
					},
					{
						"name": "cursor",
						"in": "query",
						"required": false,
						"description": "Opaque cursor returned by the previous page",
						"schema": { "type": "string" }
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": {
								"schema": { "$ref": "#/components/schemas/IntentListResponse" }
							}
						}
					},
					"400": {
						"description": "Missing userId",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication required",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Can only list own intents",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/agents/register": {
			"post": {
				"tags": ["Agents"],
				"operationId": "registerAgent",
				"summary": "Register a new agent",
				"description": "Register a new agent public key under a trustchain identity. No auth header needed — authorization is proved via an EIP-191 signature in the body (signed on the Ledger device).\n\nThe signed message must be exactly:\n```\nAuthorize agent key for Ledger Agent Payments\nKey: <agentPublicKey>\nLabel: <agentLabel>\nIdentity: <trustChainId>\n```",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/RegisterAgentRequest" },
							"examples": {
								"register": {
									"summary": "Register a trading bot",
									"value": {
										"trustChainId": "0xabcdef1234567890abcdef1234567890abcdef12",
										"agentPublicKey": "0x02abc123def456789...",
										"agentLabel": "My Trading Bot",
										"authorizationSignature": "0xsig..."
									}
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Agent registered",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/AgentResponse" } }
						}
					},
					"400": {
						"description": "Validation error or invalid signature",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Signature does not match wallet",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"409": {
						"description": "Agent public key already registered",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"429": {
						"description": "Rate limit exceeded (max 5 registrations per wallet per minute)",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"503": {
						"description": "Service temporarily unavailable",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/agents": {
			"get": {
				"tags": ["Agents"],
				"operationId": "listAgents",
				"summary": "List agents for a trustchain",
				"description": "List all agents (including revoked) for a trustchain identity. Requires session cookie; callers can only list their own agents.",
				"security": [{ "SessionCookie": [] }],
				"parameters": [
					{
						"name": "trustchainId",
						"in": "query",
						"required": true,
						"description": "Wallet address (trustchain identity)",
						"schema": { "type": "string" }
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/AgentListResponse" } }
						}
					},
					"400": {
						"description": "Missing trustchainId",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication required",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Can only list own agents",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/agents/{id}": {
			"get": {
				"tags": ["Agents"],
				"operationId": "getAgent",
				"summary": "Get agent by ID",
				"description": "Get agent details by member UUID. Requires session cookie; callers can only view their own agents.",
				"security": [{ "SessionCookie": [] }],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": { "type": "string", "format": "uuid" }
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/AgentResponse" } }
						}
					},
					"400": {
						"description": "Missing ID",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication required",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Can only view own agents",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Agent not found",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			},
			"delete": {
				"tags": ["Agents"],
				"operationId": "revokeAgentLegacy",
				"summary": "Revoke an agent (legacy DELETE)",
				"description": "Legacy alternative to POST /api/agents/revoke. Soft-revokes the agent. Requires session cookie.",
				"security": [{ "SessionCookie": [] }],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": { "type": "string", "format": "uuid" }
					}
				],
				"responses": {
					"200": {
						"description": "Agent revoked",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/AgentResponse" } }
						}
					},
					"401": {
						"description": "Authentication required",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Can only revoke own agents",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Agent not found or already revoked",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/agents/revoke": {
			"post": {
				"tags": ["Agents"],
				"operationId": "revokeAgent",
				"summary": "Revoke an agent",
				"description": "Soft-revoke an agent by member UUID. The agent will receive 401 on subsequent authenticated requests. Requires session cookie. Preferred over DELETE /api/agents/{id}.",
				"security": [{ "SessionCookie": [] }],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": { "schema": { "$ref": "#/components/schemas/RevokeAgentRequest" } }
					}
				},
				"responses": {
					"200": {
						"description": "Agent revoked",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/AgentResponse" } }
						}
					},
					"400": {
						"description": "Invalid agent ID",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"401": {
						"description": "Authentication required",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"403": {
						"description": "Can only revoke own agents",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					},
					"404": {
						"description": "Agent not found or already revoked",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/auth/challenge": {
			"post": {
				"tags": ["Auth"],
				"operationId": "authChallenge",
				"summary": "Request auth challenge",
				"description": "Issue an EIP-712 typed data challenge for wallet-based session authentication. The human signs this on their Ledger, then submits to /api/auth/verify. Challenge is valid for 5 minutes.",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/ChallengeRequest" }
						}
					}
				},
				"responses": {
					"200": {
						"description": "Challenge issued",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ChallengeResponse" } }
						}
					},
					"400": {
						"description": "Invalid wallet address",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/auth/verify": {
			"post": {
				"tags": ["Auth"],
				"operationId": "authVerify",
				"summary": "Verify auth signature",
				"description": "Verify the EIP-712 signature against the challenge and establish a session cookie (ai_session, valid 7 days).",
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": { "$ref": "#/components/schemas/VerifyRequest" }
						}
					}
				},
				"responses": {
					"200": {
						"description": "Session established",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/VerifyResponse" } }
						}
					},
					"401": {
						"description": "Invalid/expired challenge or bad signature",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		},
		"/api/auth/logout": {
			"post": {
				"tags": ["Auth"],
				"operationId": "authLogout",
				"summary": "Logout",
				"description": "Clears the ai_session cookie.",
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/LogoutResponse" } }
						}
					}
				}
			}
		},
		"/api/me": {
			"get": {
				"tags": ["Auth"],
				"operationId": "getMe",
				"summary": "Get authenticated wallet",
				"description": "Returns the wallet address from the session cookie. Requires active session.",
				"security": [{ "SessionCookie": [] }],
				"responses": {
					"200": {
						"description": "OK",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/MeResponse" } }
						}
					},
					"401": {
						"description": "No active session",
						"content": {
							"application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } }
						}
					}
				}
			}
		}
	},
	"components": {
		"securitySchemes": {
			"AgentAuth": {
				"type": "apiKey",
				"in": "header",
				"name": "Authorization",
				"description": "Agent authentication. Format: `AgentAuth <timestamp>.<bodyHash>.<signature>`\n\n- `timestamp`: Unix epoch seconds (must be within 5 min of server time)\n- `bodyHash`: keccak256 of the raw JSON body as hex string (use `0x` for GET requests)\n- `signature`: EIP-191 personal_sign of the string `<timestamp>.<bodyHash>` using the agent's secp256k1 private key\n\nThe server recovers the signer address and matches it against registered agent public keys."
			},
			"SessionCookie": {
				"type": "apiKey",
				"in": "cookie",
				"name": "ai_session",
				"description": "Session cookie established via POST /api/auth/verify. Valid for 7 days. Used by the web UI for human actions."
			}
		},
		"schemas": {
			"HealthResponse": {
				"type": "object",
				"required": ["success", "status", "timestamp"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"status": { "type": "string", "const": "ok" },
					"timestamp": { "type": "string", "format": "date-time" }
				}
			},
			"ErrorResponse": {
				"type": "object",
				"required": ["success", "error"],
				"properties": {
					"success": { "type": "boolean", "const": false },
					"error": { "type": "string", "description": "Human-readable error message" }
				}
			},
			"IntentUrgency": {
				"type": "string",
				"enum": ["low", "normal", "high", "critical"],
				"default": "normal",
				"description": "Priority level. Affects recommended polling interval."
			},
			"IntentStatus": {
				"type": "string",
				"enum": [
					"pending",
					"approved",
					"rejected",
					"broadcasting",
					"authorized",
					"executing",
					"confirmed",
					"failed",
					"expired"
				],
				"description": "Intent lifecycle status. Terminal states: rejected, confirmed, failed, expired."
			},
			"PaymentCategory": {
				"type": "string",
				"enum": [
					"api_payment",
					"subscription",
					"purchase",
					"p2p_transfer",
					"defi",
					"bill_payment",
					"donation",
					"other"
				],
				"description": "Category of payment (for display and analytics)"
			},
			"X402Resource": {
				"type": "object",
				"properties": {
					"url": { "type": "string", "format": "uri", "description": "The URL being paid for" },
					"description": { "type": "string" },
					"mimeType": { "type": "string" }
				},
				"required": ["url"]
			},
			"X402AcceptedExactEvm": {
				"type": "object",
				"description": "x402 payment requirement for EVM exact scheme (EIP-3009 TransferWithAuthorization)",
				"required": ["network", "amount", "asset", "payTo"],
				"properties": {
					"scheme": { "type": "string", "const": "exact" },
					"network": { "type": "string", "description": "CAIP-2 network ID (e.g., eip155:8453)" },
					"amount": {
						"type": "string",
						"description": "Amount in atomic units (e.g., 10000 = 0.01 USDC)"
					},
					"asset": { "type": "string", "description": "ERC-20 token contract address" },
					"payTo": { "type": "string", "description": "Recipient address" },
					"maxTimeoutSeconds": { "type": "integer" },
					"extra": {
						"type": "object",
						"properties": {
							"name": { "type": "string", "description": "EIP-712 domain name (e.g., USD Coin)" },
							"version": { "type": "string", "description": "EIP-712 domain version (e.g., 2)" },
							"decimals": { "type": "integer" }
						}
					}
				}
			},
			"X402Context": {
				"type": "object",
				"description": "Full x402 payment context stored on an intent",
				"properties": {
					"resource": { "$ref": "#/components/schemas/X402Resource" },
					"accepted": { "$ref": "#/components/schemas/X402AcceptedExactEvm" },
					"authorization": {
						"type": "object",
						"description": "EIP-3009 TransferWithAuthorization parameters (populated after signing)",
						"properties": {
							"from": { "type": "string" },
							"to": { "type": "string" },
							"value": { "type": "string" },
							"validAfter": { "type": "string" },
							"validBefore": { "type": "string" },
							"nonce": { "type": "string" }
						}
					},
					"signature": {
						"type": "string",
						"description": "EIP-712 signature of the authorization (populated after signing)"
					},
					"paymentSignatureHeader": {
						"type": "string",
						"description": "Base64-encoded PAYMENT header value the agent should send to the x402 server. Only returned to the owning agent."
					},
					"paymentPayload": {
						"type": "object",
						"description": "Structured payment payload (only returned to owning agent)"
					},
					"settlementReceipt": {
						"type": "object",
						"description": "Receipt from x402 server after settlement",
						"properties": {
							"txHash": { "type": "string" },
							"network": { "type": "string" },
							"amount": { "type": "string" },
							"settledAt": { "type": "string" },
							"blockNumber": { "type": "integer" },
							"success": { "type": "boolean" },
							"error": { "type": "string" }
						}
					},
					"expiresAt": {
						"type": "string",
						"format": "date-time",
						"description": "When the x402 authorization expires"
					}
				}
			},
			"TransferIntent": {
				"type": "object",
				"required": ["type", "token", "amount", "recipient", "chainId"],
				"properties": {
					"type": { "type": "string", "const": "transfer" },
					"token": { "type": "string", "description": "Token symbol (e.g., USDC, ETH)" },
					"tokenAddress": {
						"type": "string",
						"description": "ERC-20 contract address (auto-filled for known tokens)"
					},
					"tokenLogo": { "type": "string", "description": "URL to token logo image" },
					"amount": {
						"type": "string",
						"description": "Human-readable amount (e.g., '50', '0.01')"
					},
					"amountWei": { "type": "string", "description": "Amount in wei for precision" },
					"recipient": { "type": "string", "description": "Destination address (0x...)" },
					"recipientEns": { "type": "string", "description": "ENS name if resolved" },
					"chainId": {
						"type": "integer",
						"description": "Chain ID: 8453 (Base), 84532 (Base Sepolia), 11155111 (Sepolia), 137 (Polygon)"
					},
					"memo": { "type": "string", "description": "Human-readable reason for the transaction" },
					"resource": {
						"type": "string",
						"description": "x402: URL of the resource being paid for"
					},
					"merchant": {
						"type": "object",
						"description": "Merchant/payee information",
						"properties": {
							"name": { "type": "string" },
							"url": { "type": "string" },
							"logo": { "type": "string" },
							"verified": { "type": "boolean" }
						}
					},
					"category": { "$ref": "#/components/schemas/PaymentCategory" },
					"x402": { "$ref": "#/components/schemas/X402Context" }
				}
			},
			"PolymarketTradeDetails": {
				"type": "object",
				"required": ["type", "conditionId", "outcome", "amount", "chainId"],
				"description": "Details for a Polymarket prediction market trade. The backend enriches this with marketTitle and outcomePrice from the Gamma API.",
				"properties": {
					"type": { "type": "string", "const": "polymarket_trade" },
					"conditionId": {
						"type": "string",
						"description": "Polymarket condition ID (use GET /api/polymarket/markets to find it)"
					},
					"marketTitle": {
						"type": "string",
						"description": "Market question (auto-populated by backend from Gamma API)"
					},
					"outcome": {
						"type": "string",
						"enum": ["Yes", "No"],
						"description": "The outcome to bet on"
					},
					"amount": {
						"type": "string",
						"description": "Amount in USDC (e.g. '50', '100.50')"
					},
					"outcomePrice": {
						"type": "number",
						"description": "Current price of the chosen outcome (0–1, auto-populated by backend)"
					},
					"chainId": {
						"type": "integer",
						"const": 137,
						"description": "Must be 137 (Polygon)"
					},
					"memo": {
						"type": "string",
						"description": "Agent's justification for the trade (shown to the human signer)"
					}
				}
			},
			"PolymarketMarket": {
				"type": "object",
				"required": ["conditionId", "question", "yesPrice", "noPrice", "volume", "endDate", "active"],
				"properties": {
					"conditionId": { "type": "string", "description": "Polymarket condition ID" },
					"question": { "type": "string", "description": "Market question" },
					"yesPrice": { "type": "number", "description": "Current Yes price (0–1)" },
					"noPrice": { "type": "number", "description": "Current No price (0–1)" },
					"volume": { "type": "number", "description": "Total trading volume in USD" },
					"endDate": { "type": "string", "format": "date-time", "description": "Market end date" },
					"active": { "type": "boolean", "description": "Whether the market is currently active" }
				}
			},
			"PolymarketMarketsResponse": {
				"type": "object",
				"required": ["success", "markets"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"markets": {
						"type": "array",
						"items": { "$ref": "#/components/schemas/PolymarketMarket" }
					}
				}
			},
			"Intent": {
				"type": "object",
				"required": [
					"id",
					"userId",
					"agentId",
					"agentName",
					"details",
					"urgency",
					"status",
					"createdAt",
					"statusHistory"
				],
				"properties": {
					"id": {
						"type": "string",
						"description": "Unique intent ID (format: int_<timestamp>_<uuid>)"
					},
					"userId": { "type": "string", "description": "Wallet address of the human signer" },
					"agentId": { "type": "string", "description": "Agent identifier" },
					"agentName": { "type": "string", "description": "Agent display name" },
					"details": {
						"oneOf": [
							{ "$ref": "#/components/schemas/TransferIntent" },
							{ "$ref": "#/components/schemas/PolymarketTradeDetails" }
						],
						"discriminator": {
							"propertyName": "type",
							"mapping": {
								"transfer": "#/components/schemas/TransferIntent",
								"polymarket_trade": "#/components/schemas/PolymarketTradeDetails"
							}
						}
					},
					"urgency": { "$ref": "#/components/schemas/IntentUrgency" },
					"status": { "$ref": "#/components/schemas/IntentStatus" },
					"trustChainId": {
						"type": "string",
						"description": "Trustchain identity (wallet address) — set when created by an authenticated agent"
					},
					"createdByMemberId": {
						"type": "string",
						"format": "uuid",
						"description": "Agent member UUID"
					},
					"createdAt": { "type": "string", "format": "date-time" },
					"expiresAt": { "type": "string", "format": "date-time" },
					"reviewedAt": { "type": "string", "format": "date-time" },
					"broadcastAt": { "type": "string", "format": "date-time" },
					"confirmedAt": { "type": "string", "format": "date-time" },
					"txHash": {
						"type": "string",
						"description": "Transaction hash (populated after confirmation)"
					},
					"txUrl": { "type": "string", "description": "Block explorer URL" },
					"statusHistory": {
						"type": "array",
						"description": "Ordered audit trail of status changes",
						"items": {
							"type": "object",
							"required": ["status", "timestamp"],
							"properties": {
								"status": { "$ref": "#/components/schemas/IntentStatus" },
								"timestamp": { "type": "string", "format": "date-time" },
								"note": { "type": "string" }
							}
						}
					}
				}
			},
			"IntentResponse": {
				"type": "object",
				"required": ["success", "intent"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"intent": { "$ref": "#/components/schemas/Intent" }
				}
			},
			"CreateIntentResponse": {
				"type": "object",
				"required": ["success", "intent", "paymentUrl"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"intent": { "$ref": "#/components/schemas/Intent" },
					"paymentUrl": {
						"type": "string",
						"format": "uri",
						"description": "Direct URL for the human to review and sign this intent. Share this with the user."
					}
				}
			},
			"IntentListResponse": {
				"type": "object",
				"required": ["success", "intents"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"intents": {
						"type": "array",
						"items": { "$ref": "#/components/schemas/Intent" }
					},
					"nextCursor": {
						"type": "string",
						"description": "Opaque cursor to fetch the next page of intents"
					}
				}
			},
			"CreateIntentRequest": {
				"type": "object",
				"required": ["agentId", "details"],
				"properties": {
					"agentId": { "type": "string", "description": "Your agent's unique identifier" },
					"agentName": {
						"type": "string",
						"description": "Human-readable display name (defaults to agentId)"
					},
					"urgency": { "$ref": "#/components/schemas/IntentUrgency" },
					"expiresInMinutes": {
						"type": "integer",
						"minimum": 1,
						"description": "Minutes until intent expires (default: 1440 = 24 hours)"
					},
					"details": {
						"oneOf": [
							{ "$ref": "#/components/schemas/TransferIntent" },
							{ "$ref": "#/components/schemas/PolymarketTradeDetails" }
						],
						"discriminator": {
							"propertyName": "type",
							"mapping": {
								"transfer": "#/components/schemas/TransferIntent",
								"polymarket_trade": "#/components/schemas/PolymarketTradeDetails"
							}
						}
					}
				}
			},
			"UpdateIntentStatusRequest": {
				"type": "object",
				"required": ["status"],
				"description": "Used with PATCH /api/intents/{id}/status (legacy). Intent ID comes from the path.",
				"properties": {
					"status": { "$ref": "#/components/schemas/IntentStatus" },
					"txHash": { "type": "string", "description": "Transaction hash" },
					"note": { "type": "string", "description": "Audit note" },
					"paymentSignatureHeader": {
						"type": "string",
						"description": "x402: base64-encoded payment signature header"
					},
					"paymentPayload": {
						"type": "object",
						"additionalProperties": true,
						"description": "x402: structured payment payload"
					},
					"settlementReceipt": {
						"type": "object",
						"additionalProperties": true,
						"description": "x402: settlement receipt from server"
					}
				}
			},
			"UpdateIntentStatusByIdRequest": {
				"type": "object",
				"required": ["id", "status"],
				"description": "Used with POST /api/intents/status (preferred). Includes intent ID in the body.",
				"properties": {
					"id": { "type": "string", "description": "Intent ID (e.g., int_1707048000_abc12345)" },
					"status": { "$ref": "#/components/schemas/IntentStatus" },
					"txHash": { "type": "string", "description": "Transaction hash" },
					"note": { "type": "string", "description": "Audit note" },
					"paymentSignatureHeader": {
						"type": "string",
						"description": "x402: base64-encoded payment signature header"
					},
					"paymentPayload": {
						"type": "object",
						"additionalProperties": true,
						"description": "x402: structured payment payload"
					},
					"settlementReceipt": {
						"type": "object",
						"additionalProperties": true,
						"description": "x402: settlement receipt from server"
					},
					"expiresAt": {
						"type": "string",
						"format": "date-time",
						"description": "Override expiration timestamp"
					}
				}
			},
			"RegisterAgentRequest": {
				"type": "object",
				"required": ["trustChainId", "agentPublicKey", "authorizationSignature"],
				"properties": {
					"trustChainId": {
						"type": "string",
						"description": "Owner's wallet address (lowercased)"
					},
					"agentPublicKey": {
						"type": "string",
						"description": "0x-prefixed hex-encoded secp256k1 compressed public key"
					},
					"agentLabel": {
						"type": "string",
						"description": "Human-readable label (default: 'Unnamed Agent')"
					},
					"authorizationSignature": {
						"type": "string",
						"description": "EIP-191 personal_sign of: 'Authorize agent key for Ledger Agent Payments\\nKey: <pubkey>\\nLabel: <label>\\nIdentity: <trustchainId>'"
					}
				}
			},
			"RevokeAgentRequest": {
				"type": "object",
				"required": ["id"],
				"properties": {
					"id": { "type": "string", "format": "uuid", "description": "Agent member UUID" }
				}
			},
			"TrustchainMember": {
				"type": "object",
				"required": ["id", "trustchainId", "memberPubkey", "role", "createdAt"],
				"properties": {
					"id": { "type": "string", "format": "uuid" },
					"trustchainId": { "type": "string", "description": "Owner's wallet address" },
					"memberPubkey": {
						"type": "string",
						"description": "Hex-encoded secp256k1 compressed public key"
					},
					"role": {
						"type": "string",
						"enum": ["agent_write_only", "full_access"],
						"default": "agent_write_only"
					},
					"label": { "type": "string", "nullable": true },
					"createdAt": { "type": "string", "format": "date-time" },
					"revokedAt": {
						"type": "string",
						"format": "date-time",
						"nullable": true,
						"description": "Set when agent is revoked; null if active"
					}
				}
			},
			"AgentResponse": {
				"type": "object",
				"required": ["success", "member"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"member": { "$ref": "#/components/schemas/TrustchainMember" }
				}
			},
			"AgentListResponse": {
				"type": "object",
				"required": ["success", "members"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"members": {
						"type": "array",
						"items": { "$ref": "#/components/schemas/TrustchainMember" }
					}
				}
			},
			"ChallengeRequest": {
				"type": "object",
				"required": ["walletAddress"],
				"properties": {
					"walletAddress": { "type": "string", "description": "Ethereum wallet address (0x...)" },
					"chainId": { "type": "integer", "description": "Chain ID (default: 1)" }
				}
			},
			"ChallengeResponse": {
				"type": "object",
				"required": ["success", "challengeId", "typedData"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"challengeId": { "type": "string", "format": "uuid" },
					"typedData": { "type": "object", "description": "EIP-712 typed data to sign" }
				}
			},
			"VerifyRequest": {
				"type": "object",
				"required": ["challengeId", "signature"],
				"properties": {
					"challengeId": {
						"type": "string",
						"description": "Challenge ID from /api/auth/challenge"
					},
					"signature": {
						"type": "string",
						"description": "EIP-712 signature of the challenge typed data"
					}
				}
			},
			"VerifyResponse": {
				"type": "object",
				"required": ["success", "walletAddress"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"walletAddress": { "type": "string" }
				}
			},
			"MeResponse": {
				"type": "object",
				"required": ["success", "walletAddress"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"walletAddress": { "type": "string" }
				}
			},
			"LogoutResponse": {
				"type": "object",
				"required": ["success", "ok"],
				"properties": {
					"success": { "type": "boolean", "const": true },
					"ok": { "type": "boolean", "const": true }
				}
			}
		}
	}
}
