{"openapi":"3.0.3","info":{"title":"Pear Trading API","version":"2026-04-17.m3","description":"Edge Trading API for Pear Exchange. The full frontend reference lives in `API_REFERENCE.md` at the workspace root; this OpenAPI document is the machine-readable index."},"servers":[{"url":"https://api.pear.trade","description":"Production"},{"url":"https://api-staging.pear.trade","description":"Staging"},{"url":"http://localhost:3000","description":"Local dev"}],"components":{"securitySchemes":{"privy":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Privy access token. For live POST /execute, use the wallet-auth JWT instead."},"adminToken":{"type":"apiKey","in":"header","name":"X-Admin-Token"},"rewardsAdminToken":{"type":"apiKey","in":"header","name":"X-Rewards-Admin-Token"},"feedIngestToken":{"type":"apiKey","in":"header","name":"X-Feed-Ingest-Token"},"stripeSignature":{"type":"apiKey","in":"header","name":"Stripe-Signature"},"goldskySecret":{"type":"apiKey","in":"header","name":"X-Goldsky-Secret"}}},"tags":[{"name":"auth","description":"Privy onboarding and identity introspection."},{"name":"events","description":"Cross-venue event discovery and detail."},{"name":"markets","description":"Per-market detail, holders, recent trades."},{"name":"charts","description":"OHLC chart data and follow-trade bubble overlays."},{"name":"cost","description":"Cost rundown and routing decisions across venues."},{"name":"execute","description":"Trade placement (SSE)."},{"name":"portfolio","description":"User positions, PnL, exposure, history."},{"name":"wallets","description":"Privy wallet management and balances."},{"name":"funding","description":"Stripe onramp, cross-chain bridge intents, withdrawals."},{"name":"social","description":"Leaderboards, traders, follows, copytrade, friends-traded."},{"name":"comments","description":"Event and trade comments."},{"name":"notifications","description":"User notifications and push token registration."},{"name":"preferences","description":"User preference key-value store."},{"name":"rewards","description":"Trader cashback, multi-tier referrals, Pear Points."},{"name":"feed","description":"Followed-trader activity feed."},{"name":"dflow","description":"DFlow / Kalshi proof-of-link verification."},{"name":"insights","description":"Stats, analytics, and admin product metrics."},{"name":"ingest","description":"Server-to-server ingest webhooks (Goldsky, Stripe)."},{"name":"ops","description":"Health, pipeline freshness, OpenAPI."},{"name":"admin","description":"Admin-only operational endpoints (X-Admin-Token)."}],"paths":{"/auth/setup":{"post":{"tags":["auth"],"summary":"First-time onboarding (provisions wallets if missing)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"},"429":{"description":"Rate limited"}}}},"/auth/me":{"get":{"tags":["auth"],"summary":"Current session — userId + wallets","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"},"429":{"description":"Rate limited"}}}},"/api/events":{"get":{"tags":["events"],"summary":"List pear events (paginated)","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"maximum":2000}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"sourceType","in":"query","schema":{"type":"string","enum":["matched","polymarket_only","kalshi_only"]}}],"responses":{"200":{"description":"OK"},"304":{"description":"Not modified (ETag)"}}}},"/api/events/{slug}":{"get":{"tags":["events"],"summary":"Get event by slug","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"304":{"description":"Not modified"},"404":{"description":"Not found"}}}},"/api/events/discover":{"get":{"tags":["events"],"summary":"Event discovery feed (search + filter + sort)","parameters":[{"name":"search","in":"query","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"venue","in":"query","schema":{"type":"string","enum":["polymarket","kalshi"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["activity_desc","updated_desc","close_asc"]}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"maximum":2000}},{"name":"pageSize","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"OK"},"429":{"description":"Rate limited"}}}},"/api/events/search":{"get":{"tags":["events"],"summary":"Semantic vector search for events","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}},{"name":"threshold","in":"query","schema":{"type":"number","minimum":0,"maximum":1}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"502":{"description":"Upstream embedding failure"},"503":{"description":"Embedding key not configured"}}}},"/api/events/{eventId}/markets":{"get":{"tags":["events"],"summary":"List venue markets under a pear event","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"}},{"name":"venue","in":"query","schema":{"type":"string","enum":["polymarket","kalshi"]}},{"name":"openOnly","in":"query","schema":{"type":"boolean"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["volume_desc","updated_desc","close_asc"]}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/events/{eventId}/history":{"get":{"tags":["events","charts"],"summary":"OHLC chart history for every market under a pear event","description":"Fans out to Polymarket prices-history and Kalshi candlesticks in parallel and stitches the results into a normalized candle response. Polymarket markets return as `pointsOnly` (o=h=l=c per row, v=null) since the upstream only exposes mid/last points. Cached per (venue, marketId, interval, from, to) in Redis; TTL scales with interval (30s for 1m, 1h for 1d).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"}},{"name":"interval","in":"query","schema":{"type":"string","enum":["1m","5m","1h","1d","1w"],"default":"1h"}},{"name":"from","in":"query","schema":{"type":"integer","description":"Unix seconds. Defaults to now - lookback window for the chosen interval."}},{"name":"to","in":"query","schema":{"type":"integer","description":"Unix seconds. Defaults to now."}},{"name":"venue","in":"query","schema":{"type":"string","enum":["polymarket","kalshi"]}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/events/{eventId}/follow-trades":{"get":{"tags":["events","social","charts"],"summary":"Trade bubbles: followed traders' trades clustered by candle bucket","description":"Returns the bubbles overlay for the event chart. Joins `identity.market_trades` × `identity.follows` for the requesting user, groups trades by `(date_trunc(interval, filled_at), venue, marketId, side, outcome)` and ranks the top-N traders per bucket by size. Bubble timestamps snap to the same candle x-coordinate as `/history` so frontends can render the overlay without coordination. Polymarket markets work end-to-end; Kalshi/DFlow trades are anonymous in the public data feed (see backend caveat in modules/kalshiIngest.ts) so Kalshi bubbles will be empty until a Helius webhook is wired.","security":[{"privy":[]}],"parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"}},{"name":"interval","in":"query","schema":{"type":"string","enum":["1m","5m","1h","1d","1w"],"default":"1h"}},{"name":"from","in":"query","schema":{"type":"integer"}},{"name":"to","in":"query","schema":{"type":"integer"}},{"name":"topN","in":"query","schema":{"type":"integer","minimum":1,"maximum":20,"default":5}}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}}},"/api/markets/{venue}/{marketId}/detail":{"get":{"tags":["markets"],"summary":"Composite market detail screen contract","parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string","enum":["polymarket","kalshi"]}},{"name":"marketId","in":"path","required":true,"schema":{"type":"string"}},{"name":"chartPoints","in":"query","schema":{"type":"integer","minimum":8,"maximum":240}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/markets/{venue}/{marketId}/holders":{"get":{"tags":["markets"],"summary":"Top holders for a market","parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"marketId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"OK"}}}},"/api/markets/{venue}/{marketId}/trades":{"get":{"tags":["markets"],"summary":"Recent trades for a market","parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"marketId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/api/markets/discover":{"get":{"tags":["markets"],"summary":"[Deprecated] Use /api/events/discover","deprecated":true,"responses":{"200":{"description":"OK"}}}},"/cost-rundown":{"get":{"tags":["cost"],"summary":"Multi-venue cost preview for a pear event","parameters":[{"name":"pear_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"size","in":"query","schema":{"type":"integer","minimum":1}},{"name":"amount_usd","in":"query","schema":{"type":"number","minimum":0}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"404":{"description":"Not found"},"503":{"description":"Cost engine unavailable"}}}},"/route":{"post":{"tags":["cost"],"summary":"Smart router — pick venue + create quoted intent","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["pearId","userId","accountId"],"properties":{"pearId":{"type":"string"},"size":{"type":"number"},"userId":{"type":"string"},"accountId":{"type":"string"},"venue":{"type":"string","enum":["polymarket","kalshi"]},"marketId":{"type":"string"}}}}}},"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"404":{"description":"No route found"}}}},"/execute":{"post":{"tags":["execute"],"summary":"Execute a trade (SSE stream)","description":"Polymarket trades are proxied to the execution service; Kalshi trades are handled in-process. Live mode (mode=\"live\") requires a Privy wallet-auth JWT in the Authorization header. See API_REFERENCE.md §7.1 for the full SSE event reference.","security":[{},{"privy":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["marketId","side","outcome","size"],"properties":{"marketId":{"type":"string"},"venueMarketId":{"type":"string"},"side":{"type":"string","enum":["buy","sell"]},"outcome":{"type":"string","enum":["yes","no"]},"size":{"type":"number","minimum":0},"limitPrice":{"type":"number","minimum":0,"maximum":1},"orderType":{"type":"string","enum":["limit","market"]},"mode":{"type":"string","enum":["simulation","live"]},"venue":{"type":"string","enum":["polymarket","kalshi"]},"pearId":{"type":"string"},"fundingSourceChain":{"type":"string","enum":["polygon","solana"]},"fundingSourceCurrency":{"type":"string"},"fundingAmountAtomic":{"type":"string"},"referrerId":{"type":"string"},"eventTitle":{"type":"string"},"marketTitle":{"type":"string"},"eventSlug":{"type":"string"},"category":{"type":"string"}}}}}},"responses":{"200":{"description":"Server-Sent Events stream","content":{"text/event-stream":{}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"502":{"description":"Execution service upstream failure"},"503":{"description":"Execution service not configured"}}}},"/api/positions/{positionId}/sell":{"post":{"tags":["execute"],"summary":"Build a sell from an existing position and proxy /execute","security":[{"privy":[]}],"parameters":[{"name":"positionId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"size":{"type":"number"},"orderType":{"type":"string","enum":["market","limit"]},"limitPrice":{"type":"number","minimum":0,"maximum":1},"mode":{"type":"string","enum":["live","simulation"]}}}}}},"responses":{"200":{"description":"Server-Sent Events stream","content":{"text/event-stream":{}}},"400":{"description":"Bad request"},"404":{"description":"Not found"}}}},"/api/portfolio/overview":{"get":{"tags":["portfolio"],"summary":"Portfolio screen contract","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/portfolio":{"get":{"tags":["portfolio"],"summary":"Open positions across wallets","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/portfolio/summary":{"get":{"tags":["portfolio"],"summary":"Aggregated PnL / exposure / fees","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/portfolio/history":{"get":{"tags":["portfolio"],"summary":"Trade history merged across wallets","security":[{"privy":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/api/portfolio/analytics":{"get":{"tags":["portfolio"],"summary":"Category win rate, exposure, weekly rank","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/positions":{"get":{"tags":["portfolio"],"summary":"User positions with status filter","security":[{"privy":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["open","closed","pending"]}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/api/positions/{positionId}":{"get":{"tags":["portfolio"],"summary":"Single position with linked orders","security":[{"privy":[]}],"parameters":[{"name":"positionId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/positions/by-market/{venue}/{marketId}":{"get":{"tags":["portfolio"],"summary":"User's positions on one specific market","security":[{"privy":[]}],"parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"marketId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/orders":{"get":{"tags":["portfolio"],"summary":"Caller's orders (with optional state filter)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/wallets":{"get":{"tags":["wallets"],"summary":"List wallets across chains","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/wallets/import":{"post":{"tags":["wallets"],"summary":"Link an external wallet","security":[{"privy":[]}],"responses":{"201":{"description":"Created"},"400":{"description":"Bad request"},"409":{"description":"Already linked"}}}},"/api/wallets/{id}":{"delete":{"tags":["wallets"],"summary":"Remove a linked wallet (privy/turnkey wallets cannot be removed)","security":[{"privy":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"404":{"description":"Not found"}}}},"/api/wallets/balance":{"get":{"tags":["wallets"],"summary":"Aggregate USD balance","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/wallets/balance/polygon":{"get":{"tags":["wallets"],"summary":"Polygon per-wallet balance (pUSD/USDC + MATIC)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/wallets/balance/solana":{"get":{"tags":["wallets"],"summary":"Solana per-wallet balance (USDC + SOL)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/funding/onramp":{"post":{"tags":["funding"],"summary":"Stripe Crypto Onramp session","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"429":{"description":"Rate limited"}}}},"/api/funding/intent":{"post":{"tags":["funding"],"summary":"[Legacy] Stripe PaymentIntent fiat onramp","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/funding/history":{"get":{"tags":["funding"],"summary":"Onramp/funding history","security":[{"privy":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}}],"responses":{"200":{"description":"OK"}}}},"/api/funding/withdraw":{"post":{"tags":["funding"],"summary":"Queue a withdrawal","security":[{"privy":[]}],"responses":{"201":{"description":"Created"},"400":{"description":"Bad request"}}}},"/api/funding/withdrawals":{"get":{"tags":["funding"],"summary":"Withdrawal history","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/webhooks/stripe":{"post":{"tags":["funding"],"summary":"Stripe → Pear webhook (server-to-server)","security":[{"stripeSignature":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"500":{"description":"Webhook secret missing"}}}},"/webhooks/goldsky/polymarket":{"post":{"tags":["ingest"],"summary":"Goldsky → Pear Polymarket fill webhook (server-to-server)","description":"Receives Polygon `OrderFilled` events filtered by Goldsky's Turbo Pipeline against the `identity.tracked_wallets` allowlist. Writes through to `identity.market_trades`. Idempotent — at-least-once delivery is absorbed by an upsert key on `(venue, venue_market_id, venue_trader_id, side, filled_at)`.","security":[{"goldskySecret":[]}],"responses":{"200":{"description":"Batch acknowledged. Body: `{received, skipped}`"},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"500":{"description":"GOLDSKY_WEBHOOK_SECRET missing"}}}},"/api/home":{"get":{"tags":["social"],"summary":"Home screen contract","security":[{"privy":[]}],"parameters":[{"name":"category","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/social/overview":{"get":{"tags":["social"],"summary":"Social discovery hub","responses":{"200":{"description":"OK"}}}},"/api/social/copytrade":{"post":{"tags":["social"],"summary":"Mirror a trade from a followed trader (with risk caps)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"404":{"description":"Not found"},"429":{"description":"Rate limited"},"502":{"description":"Execution upstream failure"}}}},"/api/follows":{"post":{"tags":["social"],"summary":"Follow a trader","security":[{"privy":[]}],"responses":{"201":{"description":"Created"},"400":{"description":"Bad request"}}},"get":{"tags":["social"],"summary":"List caller's follows","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/follows/{venue}/{traderId}":{"delete":{"tags":["social"],"summary":"Unfollow a trader","security":[{"privy":[]}],"parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"traderId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/leaderboard":{"get":{"tags":["social"],"summary":"Trader leaderboard","parameters":[{"name":"sort_by","in":"query","schema":{"type":"string","enum":["total_volume","total_pnl","win_count","total_trades"]}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}},{"name":"venue","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/leaderboard/me":{"get":{"tags":["social"],"summary":"Caller's rank + neighbours","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/leaderboard/refresh":{"post":{"tags":["admin"],"summary":"Admin: refresh stale trader_stats rows","security":[{"privy":[],"adminToken":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"}}}},"/api/traders/{venue}/{traderId}":{"get":{"tags":["social"],"summary":"Trader profile + stats","parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"traderId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/traders/{venue}/{traderId}/trades":{"get":{"tags":["social"],"summary":"Trader's recent trades","parameters":[{"name":"venue","in":"path","required":true,"schema":{"type":"string"}},{"name":"traderId","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer"}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/api/stats":{"get":{"tags":["social"],"summary":"Caller's public stats","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/stats/{userId}":{"get":{"tags":["social"],"summary":"Public stats for any user","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/social/friends-traded":{"post":{"tags":["social"],"summary":"Which followed traders have positions on these markets","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"}}}},"/api/product/metrics":{"get":{"tags":["admin"],"summary":"Admin: product metrics","security":[{"privy":[],"adminToken":[]}],"responses":{"200":{"description":"OK"}}}},"/api/events/{slug}/comments":{"get":{"tags":["comments"],"summary":"Comments for an event","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["recent","top"]}}],"responses":{"200":{"description":"OK"}}},"post":{"tags":["comments"],"summary":"Create comment on an event","security":[{"privy":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created"},"400":{"description":"Bad request"},"404":{"description":"Not found"}}}},"/api/trades/{tradeId}/comments":{"get":{"tags":["comments"],"summary":"Comments on a trade","parameters":[{"name":"tradeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}},"post":{"tags":["comments"],"summary":"Create comment on a trade","security":[{"privy":[]}],"parameters":[{"name":"tradeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"Created"},"404":{"description":"Not found"}}}},"/api/comments/{commentId}/replies":{"get":{"tags":["comments"],"summary":"Replies to a comment","parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/comments/{commentId}":{"delete":{"tags":["comments"],"summary":"Delete own comment","security":[{"privy":[]}],"parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"403":{"description":"Not owner"},"404":{"description":"Not found"}}}},"/api/comments/{commentId}/like":{"post":{"tags":["comments"],"summary":"Like a comment","security":[{"privy":[]}],"parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}},"delete":{"tags":["comments"],"summary":"Unlike a comment","security":[{"privy":[]}],"parameters":[{"name":"commentId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/notifications":{"get":{"tags":["notifications"],"summary":"Notification inbox","security":[{"privy":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer"}},{"name":"unread","in":"query","schema":{"type":"string","enum":["true"]}}],"responses":{"200":{"description":"OK"}}}},"/api/notifications/read":{"post":{"tags":["notifications"],"summary":"Mark notifications read (omit ids = mark all)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/push-tokens":{"post":{"tags":["notifications"],"summary":"Register a push token","security":[{"privy":[]}],"responses":{"201":{"description":"Created"},"400":{"description":"Bad request"}}},"delete":{"tags":["notifications"],"summary":"Unregister a push token","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/preferences":{"get":{"tags":["preferences"],"summary":"Get user preferences","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}},"put":{"tags":["preferences"],"summary":"Update user preferences (partial)","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"}}}},"/api/rewards/summary":{"get":{"tags":["rewards"],"summary":"Cashback + referral USD totals","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"503":{"description":"Supabase unavailable"}}}},"/api/rewards/history":{"get":{"tags":["rewards"],"summary":"Reward ledger rows (paginated)","security":[{"privy":[]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK"}}}},"/api/rewards/referral/status":{"get":{"tags":["rewards"],"summary":"Referral code + active referral","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/rewards/referral/code":{"post":{"tags":["rewards"],"summary":"Get/create caller's referral code","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/rewards/referral/apply":{"post":{"tags":["rewards"],"summary":"Apply someone else's referral code","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"409":{"description":"Conflict (loop / already referred / first fee charged)"}}}},"/api/rewards/claim":{"post":{"tags":["rewards"],"summary":"Claim available rewards to a wallet","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"429":{"description":"Rate limited"}}}},"/api/rewards/audit":{"get":{"tags":["rewards"],"summary":"User-facing reward audit trail","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/api/rewards/admin/reconciliation":{"get":{"tags":["admin"],"summary":"Admin: reconciliation summary","security":[{"rewardsAdminToken":[]}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized"}}}},"/api/rewards/admin/reconciliation/export":{"get":{"tags":["admin"],"summary":"Admin: reconciliation CSV","security":[{"rewardsAdminToken":[]}],"responses":{"200":{"description":"CSV","content":{"text/csv":{}}}}}},"/api/rewards/admin/reconciliation/replay":{"post":{"tags":["admin"],"summary":"Admin: replay reconciliation for a window","security":[{"rewardsAdminToken":[]}],"responses":{"200":{"description":"OK"}}}},"/api/feed":{"get":{"tags":["feed"],"summary":"Paginated feed (cursor-based)","parameters":[{"name":"category","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","schema":{"type":"string"}},{"name":"pear_event_id","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":200}},{"name":"cursor","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/feed/{id}":{"get":{"tags":["feed"],"summary":"Single feed item","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"Not found"}}}},"/api/feed/quality":{"get":{"tags":["feed"],"summary":"Feed mapping/quality metrics","responses":{"200":{"description":"OK"}}}},"/api/feed/stream":{"get":{"tags":["feed"],"summary":"SSE stream of new feed items","responses":{"200":{"description":"Server-Sent Events stream","content":{"text/event-stream":{}}}}}},"/api/feed/ingest":{"post":{"tags":["admin"],"summary":"Worker: ingest feed items","security":[{"feedIngestToken":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"}}}},"/dflow/proof-link":{"post":{"tags":["dflow"],"summary":"Generate DFlow Proof deep-link","security":[{"privy":[]}],"responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"503":{"description":"Privy failure"}}}},"/dflow/verify/{wallet}":{"get":{"tags":["dflow"],"summary":"Check wallet DFlow Proof status","parameters":[{"name":"wallet","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"502":{"description":"Upstream proof API failure"}}}},"/dflow/proof-status":{"post":{"tags":["dflow"],"summary":"Proof status for caller's primary Solana wallet","security":[{"privy":[]}],"responses":{"200":{"description":"OK"}}}},"/stream/trades":{"get":{"tags":["execute"],"summary":"SSE stream of all live Polymarket + Kalshi trades","responses":{"200":{"description":"Server-Sent Events stream","content":{"text/event-stream":{}}}}}},"/health":{"get":{"tags":["ops"],"summary":"In-process health snapshot (no DB)","responses":{"200":{"description":"OK"}}}},"/health/pipeline":{"get":{"tags":["ops"],"summary":"DB-backed pipeline health","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/cache-policy":{"get":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: cache TTL/invalidation policy","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/hot-cold-paths":{"get":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: hot/cold path overview","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/data-quality":{"get":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: data quality dashboard","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/migration-safety":{"get":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: migration safety status","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/dlq":{"get":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: dead-letter queue contents","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/dlq/replay":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: replay DLQ entries","responses":{"200":{"description":"OK"},"400":{"description":"Bad request"}}}},"/api/admin/storage/migration-safety/preflight":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: migration preflight checks","responses":{"200":{"description":"OK"}}}},"/api/admin/storage/cache/invalidate":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: invalidate Redis cache keys/patterns (use after direct DB seeds)","responses":{"200":{"description":"OK"},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"}}}},"/api/admin/resolve-markets":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: settle resolved markets","responses":{"200":{"description":"OK"}}}},"/api/admin/update-prices":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: mark-to-market open positions","responses":{"200":{"description":"OK"}}}},"/api/admin/compute-leaderboard":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: recompute leaderboard ranks","responses":{"200":{"description":"OK"}}}},"/api/admin/tag-categories":{"post":{"tags":["admin"],"security":[{"adminToken":[]}],"summary":"Admin: tag user category preferences","responses":{"200":{"description":"OK"}}}}}}