{
  "openapi": "3.1.0",
  "info": {
    "title": "PolyZig Platform API",
    "version": "1.1.0",
    "description": "Public HTTP API for the PolyZig copy-trading platform. Designed to be safely consumed by AI agents, automation scripts, and third-party integrations. See `https://polyzig.com/llms-full.txt` for an agent-oriented integration guide.\n\n## Authentication\n\nTwo bearer credentials are accepted on `Authorization: Bearer <token>`:\n\n1. **JWT** (first-party clients) — issued by `/api/auth/magic` or `/api/auth/verify-code`. 24h expiry, full account scope.\n2. **API key** (`pzk_*`, recommended for agents) — minted from the user dashboard with explicit scopes and independent revocation.\n\n## Idempotency\n\nState-changing endpoints accept an `Idempotency-Key` request header. Re-submitting the same key within 24h returns the cached response instead of re-executing. Generate a fresh UUID per logical operation and retry with the same key on transport failure.\n\n## Rate limits\n\nDefault: 60 req/s/IP, 500 burst. Responses include `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`. 429 / 503 responses include `Retry-After` (seconds).\n\n## Errors\n\nAll error responses share the shape `{ \"error\": \"...\", \"code\": \"machine_readable_code\" }`. Branch on `code`, not on `error` (which may be localized).",
    "contact": {
      "name": "PolyZig support",
      "email": "support@polyzig.com",
      "url": "https://polyzig.com/support"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://polyzig.com/terms"
    }
  },
  "servers": [
    { "url": "https://api.polyzig.com", "description": "Production" }
  ],
  "tags": [
    { "name": "auth", "description": "Login, signup, identity providers" },
    { "name": "users", "description": "Current user profile" },
    { "name": "wallets", "description": "Polygon wallet + trading credentials" },
    { "name": "keys", "description": "API key management for agent access" },
    { "name": "stats", "description": "Platform + per-user transparency metrics" },
    { "name": "markets", "description": "Polymarket market data + order entry" },
    { "name": "positions", "description": "Open positions, claims, sells" },
    { "name": "paper", "description": "Paper-trading positions + history" },
    { "name": "trades", "description": "Executed trade history" },
    { "name": "configs", "description": "Copy-trading config CRUD + lifecycle" },
    { "name": "subscription", "description": "Tier, billing, treasury balance" },
    { "name": "notifications", "description": "User notifications" },
    { "name": "arbitrage", "description": "AI arbitrage scanner (HFT tier)" },
    { "name": "mcp", "description": "Model Context Protocol server endpoint" }
  ],
  "security": [
    { "bearerJwt": [] },
    { "apiKey": [] }
  ],
  "components": {
    "securitySchemes": {
      "bearerJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Session JWT from `/api/auth/magic` or `/api/auth/verify-code`. Full account scope, 24h expiry."
      },
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "pzk_*",
        "description": "Long-lived API key minted from the dashboard. Carries explicit scopes (e.g., `read:positions`, `trade:execute`). Revocable independently of the user's session."
      }
    },
    "parameters": {
      "idempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "schema": { "type": "string", "format": "uuid" },
        "required": false,
        "description": "Unique key for safe retries on state-changing requests. Cached for 24 hours."
      },
      "limit": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 50 },
        "required": false
      },
      "offset": {
        "name": "offset",
        "in": "query",
        "schema": { "type": "integer", "minimum": 0, "default": 0 },
        "required": false
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": ["error", "code"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message. May be localized. Do not parse."
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code. Source of truth for branching.",
            "enum": [
              "unauthorized",
              "not_found",
              "bad_request",
              "validation_failed",
              "database_error",
              "cache_error",
              "invalid_token",
              "crypto_error",
              "wallet_error",
              "internal_error",
              "polymarket_unavailable",
              "tier_limit_exceeded",
              "insufficient_balance",
              "subscription_error",
              "payment_failed",
              "feature_not_enabled",
              "rate_limited",
              "scope_required"
            ]
          }
        }
      },
      "Pagination": {
        "type": "object",
        "required": ["limit", "offset", "has_next"],
        "properties": {
          "limit": { "type": "integer", "example": 50 },
          "offset": { "type": "integer", "example": 0 },
          "has_next": { "type": "boolean", "example": false }
        }
      },
      "DecimalString": {
        "type": "string",
        "pattern": "^-?[0-9]+(\\.[0-9]+)?$",
        "description": "Decimal serialized as a string to preserve precision. Parse with a fixed-decimal library, not float.",
        "example": "1234.56"
      },
      "PlatformStats": {
        "type": "object",
        "properties": {
          "median_latency_ms": { "type": "number", "example": 412 },
          "p95_latency_ms": { "type": "number", "example": 680 },
          "total_users": { "type": "integer" },
          "total_trades_24h": { "type": "integer" },
          "total_volume_usdc_24h": { "$ref": "#/components/schemas/DecimalString" }
        }
      },
      "UserSummary": {
        "type": "object",
        "properties": {
          "user_id": { "type": "string", "format": "uuid" },
          "total_pnl": { "$ref": "#/components/schemas/DecimalString" },
          "realized_pnl": { "$ref": "#/components/schemas/DecimalString" },
          "trade_count": { "type": "integer" },
          "fees_paid": { "$ref": "#/components/schemas/DecimalString" }
        }
      },
      "Position": {
        "type": "object",
        "properties": {
          "token_id": { "type": "string" },
          "market_slug": { "type": "string" },
          "market_title": { "type": "string" },
          "outcome": { "type": "string", "example": "Yes" },
          "size": { "$ref": "#/components/schemas/DecimalString" },
          "cost_basis": { "$ref": "#/components/schemas/DecimalString" },
          "current_price": { "$ref": "#/components/schemas/DecimalString" },
          "current_value": { "$ref": "#/components/schemas/DecimalString" },
          "unrealized_pnl": { "$ref": "#/components/schemas/DecimalString" },
          "resolved": { "type": "boolean" },
          "claimable_value": { "$ref": "#/components/schemas/DecimalString", "nullable": true }
        }
      },
      "PositionList": {
        "type": "object",
        "required": ["positions", "pagination"],
        "properties": {
          "positions": { "type": "array", "items": { "$ref": "#/components/schemas/Position" } },
          "pagination": { "$ref": "#/components/schemas/Pagination" }
        }
      },
      "Trade": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "token_id": { "type": "string" },
          "market_slug": { "type": "string" },
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "size": { "$ref": "#/components/schemas/DecimalString" },
          "price": { "$ref": "#/components/schemas/DecimalString" },
          "filled_price": { "$ref": "#/components/schemas/DecimalString" },
          "usdc_value": { "$ref": "#/components/schemas/DecimalString" },
          "fee": { "$ref": "#/components/schemas/DecimalString" },
          "latency_ms": { "type": "integer", "nullable": true },
          "status": { "type": "string", "enum": ["pending", "filled", "cancelled", "failed"] },
          "created_at": { "type": "string", "format": "date-time" },
          "filled_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "TradeList": {
        "type": "object",
        "required": ["trades", "pagination"],
        "properties": {
          "trades": { "type": "array", "items": { "$ref": "#/components/schemas/Trade" } },
          "pagination": { "$ref": "#/components/schemas/Pagination" }
        }
      },
      "Order": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string" },
          "token_id": { "type": "string" },
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "price": { "$ref": "#/components/schemas/DecimalString" },
          "size": { "$ref": "#/components/schemas/DecimalString" },
          "filled_size": { "$ref": "#/components/schemas/DecimalString" },
          "status": { "type": "string" },
          "order_type": { "type": "string", "enum": ["market", "limit"] },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "PlaceOrderRequest": {
        "type": "object",
        "required": ["token_id", "side", "size", "order_type"],
        "properties": {
          "token_id": { "type": "string" },
          "side": { "type": "string", "enum": ["buy", "sell"] },
          "size": { "$ref": "#/components/schemas/DecimalString" },
          "price": { "$ref": "#/components/schemas/DecimalString", "description": "Required for limit orders." },
          "order_type": { "type": "string", "enum": ["market", "limit"] }
        }
      },
      "PlaceOrderResponse": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string" },
          "status": { "type": "string", "enum": ["accepted", "filled", "rejected"] },
          "filled_size": { "$ref": "#/components/schemas/DecimalString" },
          "average_price": { "$ref": "#/components/schemas/DecimalString" }
        }
      },
      "CopyConfig": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "target_address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" },
          "size_multiplier": { "$ref": "#/components/schemas/DecimalString" },
          "max_position_usdc": { "$ref": "#/components/schemas/DecimalString" },
          "price_deviation_bps": { "type": "integer" },
          "blocked_categories": { "type": "array", "items": { "type": "string" } },
          "take_profit_bps": { "type": "integer", "nullable": true },
          "stop_loss_bps": { "type": "integer", "nullable": true },
          "is_active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "MarketTranslation": {
        "type": "object",
        "properties": {
          "slug": { "type": "string" },
          "locale": { "type": "string", "example": "fr" },
          "title": { "type": "string" },
          "description": { "type": "string" }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "name": { "type": "string", "example": "claude-agent" },
          "prefix": { "type": "string", "example": "pzk_a1b2c3..." },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read:account",
                "read:positions",
                "read:trades",
                "read:markets",
                "trade:execute",
                "trade:cancel",
                "wallet:write"
              ]
            }
          },
          "created_at": { "type": "string", "format": "date-time" },
          "last_used_at": { "type": "string", "format": "date-time", "nullable": true },
          "expires_at": { "type": "string", "format": "date-time", "nullable": true },
          "revoked_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "ApiKeyCreateRequest": {
        "type": "object",
        "required": ["name", "scopes"],
        "properties": {
          "name": { "type": "string", "minLength": 1, "maxLength": 80 },
          "scopes": {
            "type": "array",
            "items": { "type": "string" },
            "minItems": 1
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "nullable": true,
            "description": "Lifetime of the new key in days. `0` means the key never expires (still revocable from the dashboard). Omit to use the server default (90 days). Range matches the dashboard `Never` option and the backend's accepted values."
          }
        }
      },
      "ApiKeyCreateResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/ApiKey" },
          {
            "type": "object",
            "required": ["secret"],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The full bearer token. Shown ONCE at creation time. Store it immediately — it cannot be retrieved later.",
                "example": "pzk_live_a1b2c3d4e5f6..."
              }
            }
          }
        ]
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Authenticated but missing scope or tier.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Rate limit exceeded.",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds until retry is permitted." }
        },
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "paths": {
    "/api/stats/platform": {
      "get": {
        "tags": ["stats"],
        "summary": "Public platform-wide stats",
        "description": "Latency, volume, user count. No auth required. Safe entry point for crawlers.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlatformStats" } } }
          }
        }
      }
    },
    "/api/stats/summary": {
      "get": {
        "tags": ["stats"],
        "summary": "Current user's PnL summary",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserSummary" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/stats/latency": {
      "get": {
        "tags": ["stats"],
        "summary": "Per-user latency stats (last 7d)",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": {
          "200": { "description": "OK" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/markets/translations": {
      "get": {
        "tags": ["markets"],
        "summary": "Batch market translations",
        "description": "Returns translated market metadata for a list of slugs. No auth required.",
        "security": [],
        "parameters": [
          { "name": "slugs", "in": "query", "schema": { "type": "string" }, "description": "Comma-separated market slugs" },
          { "name": "locale", "in": "query", "schema": { "type": "string", "example": "fr" } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "translations": { "type": "array", "items": { "$ref": "#/components/schemas/MarketTranslation" } }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/markets/translations/search": {
      "get": {
        "tags": ["markets"],
        "summary": "Search markets by title",
        "security": [],
        "parameters": [
          { "name": "q", "in": "query", "required": true, "schema": { "type": "string" } },
          { "name": "locale", "in": "query", "schema": { "type": "string" } },
          { "$ref": "#/components/parameters/limit" }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/markets/order": {
      "post": {
        "tags": ["markets"],
        "summary": "Place market or limit order",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceOrderRequest" } } }
        },
        "responses": {
          "200": {
            "description": "Order accepted or filled",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PlaceOrderResponse" } } }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/markets/orders": {
      "get": {
        "tags": ["markets"],
        "summary": "List open CLOB orders",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:markets"] }],
        "responses": { "200": { "description": "OK" } }
      },
      "delete": {
        "tags": ["markets"],
        "summary": "Cancel all open orders",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:cancel"] }],
        "responses": { "200": { "description": "Cancelled" } }
      }
    },
    "/api/markets/orders/{order_id}": {
      "delete": {
        "tags": ["markets"],
        "summary": "Cancel a single order",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:cancel"] }],
        "parameters": [
          { "name": "order_id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": { "200": { "description": "Cancelled" } }
      }
    },
    "/api/positions": {
      "get": {
        "tags": ["positions"],
        "summary": "List current user's open Polymarket positions",
        "description": "Returns a bare JSON array of positions, optionally filtered by `active_only` (defaults to true — only positions with shares > 0). The handler returns the full filtered set in a single response, so this endpoint does **not** paginate; do not send `limit`/`offset` and do not expect `X-Pagination-*` headers. Use `/api/trades` (which does paginate) when you need windowed results.",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:positions"] }],
        "parameters": [
          {
            "name": "active_only",
            "in": "query",
            "schema": { "type": "boolean", "default": true },
            "description": "Filter to positions with shares > 0. Set false to include closed positions."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Position" }
                }
              }
            }
          }
        }
      }
    },
    "/api/positions/claim": {
      "post": {
        "tags": ["positions"],
        "summary": "Redeem resolved positions",
        "description": "Submits on-chain redemption transactions for resolved positions. **Idempotent** via `Idempotency-Key` header.",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["wallet:write"] }],
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token_ids": {
                    "type": "array",
                    "items": { "type": "string" },
                    "description": "Optional: subset of token IDs to claim. If omitted, claims all claimable positions."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Claim transactions submitted" },
          "402": {
            "description": "Insufficient gas balance",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
          }
        }
      }
    },
    "/api/positions/sell": {
      "post": {
        "tags": ["positions"],
        "summary": "Sell open positions at market",
        "description": "Places CLOB sell orders for the listed token IDs. This is order entry (it writes to the book), so it's gated on `trade:execute` — not `wallet:write` (which is reserved for on-chain redemption / withdraw).",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["token_ids"],
                "properties": {
                  "token_ids": { "type": "array", "items": { "type": "string" } }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Sell orders placed" } }
      }
    },
    "/api/paper/positions": {
      "get": {
        "tags": ["paper"],
        "summary": "List paper-trading positions",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:positions"] }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/paper/trades": {
      "get": {
        "tags": ["paper"],
        "summary": "List paper trades",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:trades"] }],
        "parameters": [
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/trades": {
      "get": {
        "tags": ["trades"],
        "summary": "Trade history",
        "description": "Bare JSON array of trades, newest first. Pagination conveyed in response headers (`X-Pagination-*`).",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:trades"] }],
        "parameters": [
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" },
          { "name": "config_id", "in": "query", "schema": { "type": "string", "format": "uuid" }, "description": "Filter by copy-config ID" }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Pagination-Limit": { "schema": { "type": "integer" }, "description": "The `limit` applied to this query." },
              "X-Pagination-Offset": { "schema": { "type": "integer" }, "description": "The `offset` applied to this query." },
              "X-Pagination-Count": { "schema": { "type": "integer" }, "description": "Number of items in this response page." },
              "X-Pagination-Has-Next": { "schema": { "type": "string", "enum": ["true", "false"] }, "description": "`true` if a follow-up page is likely (cheap heuristic — full page implies more)." }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Trade" }
                }
              }
            }
          }
        }
      }
    },
    "/api/trades/{id}": {
      "get": {
        "tags": ["trades"],
        "summary": "Single trade detail",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:trades"] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Trade" } } }
          },
          "404": { "description": "Trade not found" }
        }
      }
    },
    "/api/configs": {
      "get": {
        "tags": ["configs"],
        "summary": "List copy-trading configs",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": { "200": { "description": "OK" } }
      },
      "post": {
        "tags": ["configs"],
        "summary": "Create copy-trading config",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CopyConfig" } } }
        },
        "responses": { "201": { "description": "Created" } }
      }
    },
    "/api/configs/{id}": {
      "get": {
        "tags": ["configs"],
        "summary": "Get config",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      },
      "put": {
        "tags": ["configs"],
        "summary": "Update config",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      },
      "delete": {
        "tags": ["configs"],
        "summary": "Delete config",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "204": { "description": "Deleted" } }
      }
    },
    "/api/configs/{id}/start": {
      "post": {
        "tags": ["configs"],
        "summary": "Start copying",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/configs/{id}/stop": {
      "post": {
        "tags": ["configs"],
        "summary": "Stop copying",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["trade:execute"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/configs/{id}/pnl": {
      "get": {
        "tags": ["configs"],
        "summary": "Config-level PnL summary",
        "description": "Realized + unrealized PnL aggregated for one copy-trading config, with a per-position breakdown. Useful for agents deciding whether to keep, tune, or stop a config.",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/configs/{id}/trades": {
      "get": {
        "tags": ["configs"],
        "summary": "Trades attributed to this config",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:trades"] }],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } },
          { "$ref": "#/components/parameters/limit" },
          { "$ref": "#/components/parameters/offset" }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/configs/{id}/status": {
      "get": {
        "tags": ["configs"],
        "summary": "Live executor status for a copy config",
        "description": "Returns whether the per-config executor is running, last-error info, and recent fill counts. Use to surface stuck-config conditions to the user.",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/helpers/suggest-multiplier": {
      "get": {
        "tags": ["configs"],
        "summary": "Suggest a sizing multiplier",
        "description": "Server-side helper that returns a recommended `size_multiplier` based on the user's current balance. Read-only; does not create a config.",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:markets"] }],
        "parameters": [
          { "name": "balance", "in": "query", "schema": { "type": "number" }, "description": "Optional balance override (USDC). Defaults to live tier-service balance." }
        ],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/users/me": {
      "get": {
        "tags": ["users"],
        "summary": "Current user profile",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/wallets/balance": {
      "get": {
        "tags": ["wallets"],
        "summary": "Wallet balances (pUSD, USDC, MATIC)",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/wallets/deposit-address": {
      "get": {
        "tags": ["wallets"],
        "summary": "Deposit address for funding",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["read:account"] }],
        "responses": { "200": { "description": "OK" } }
      }
    },
    "/api/trading-wallet/withdraw": {
      "post": {
        "tags": ["wallets"],
        "summary": "Withdraw funds from trading wallet",
        "security": [{ "bearerJwt": [] }, { "apiKey": ["wallet:write"] }],
        "parameters": [{ "$ref": "#/components/parameters/idempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["to_address", "amount", "token"],
                "properties": {
                  "to_address": {
                    "type": "string",
                    "pattern": "^0x[0-9a-fA-F]{40}$",
                    "description": "Destination Polygon address."
                  },
                  "amount": {
                    "$ref": "#/components/schemas/DecimalString",
                    "description": "Decimal-string amount to withdraw."
                  },
                  "token": {
                    "type": "string",
                    "enum": ["usdc", "usdc_e", "matic"],
                    "description": "Which on-chain asset to withdraw. `usdc` = pUSD."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Withdrawal submitted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "tx_hash": { "type": "string", "nullable": true },
                    "message": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/keys": {
      "get": {
        "tags": ["keys"],
        "summary": "List API keys for current user",
        "description": "Returns a bare JSON array of ApiKey objects. The plaintext secret is never included — only metadata + the safe-to-show `prefix`. Sorted newest-first.",
        "security": [{ "bearerJwt": [] }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/ApiKey" }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["keys"],
        "summary": "Mint a new API key",
        "description": "Returns the full secret ONCE. Store it immediately — it cannot be retrieved later.",
        "security": [{ "bearerJwt": [] }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiKeyCreateRequest" } } }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiKeyCreateResponse" } } }
          }
        }
      }
    },
    "/api/keys/{id}": {
      "delete": {
        "tags": ["keys"],
        "summary": "Revoke an API key",
        "security": [{ "bearerJwt": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
        "responses": { "204": { "description": "Revoked" } }
      }
    },
    "/api/mcp": {
      "post": {
        "tags": ["mcp"],
        "summary": "Model Context Protocol JSON-RPC endpoint",
        "description": "MCP server entry point. Speaks JSON-RPC 2.0 over the MCP Streamable HTTP transport. Authenticate with a `pzk_*` API key — JWTs are rejected here to keep agent automation off browser-session tokens.\n\n### Available tools (v1)\n\nThe server returns only the subset of tools your API key's scopes permit.\n\n**Discovery / unauthenticated-scope:**\n- `get_platform_stats` — public latency + volume metrics\n- `search_markets` — full-text Polymarket market search (read:markets)\n\n**Per-user reads:**\n- `get_user_summary` (read:account)\n- `list_open_positions` (read:positions)\n- `list_paper_positions` (read:positions)\n- `list_trades` (read:trades)\n\n**Copy-trading reads:**\n- `list_copy_configs` (read:account)\n- `get_copy_config` (read:account)\n- `get_config_pnl` (read:account)\n- `get_config_trades` (read:trades)\n- `suggest_multiplier` (read:markets)\n\n**Copy-trading writes:**\n- `create_copy_config` (trade:execute)\n- `start_copying` (trade:execute)\n- `stop_copying` (trade:execute)\n- `delete_copy_config` (trade:execute)\n\n**Direct trading:**\n- `place_market_order` (trade:execute)\n- `list_open_orders` (read:markets)\n- `cancel_order` (trade:cancel)\n- `claim_positions` (wallet:write)\n\nFull MCP manifest at `https://polyzig.com/.well-known/mcp.json`.",
        "security": [{ "apiKey": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["jsonrpc", "method"],
                "properties": {
                  "jsonrpc": { "type": "string", "const": "2.0" },
                  "id": { "oneOf": [{ "type": "string" }, { "type": "integer" }, { "type": "null" }] },
                  "method": { "type": "string" },
                  "params": { "type": "object" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "JSON-RPC response" } }
      }
    }
  }
}
