SURVEILLANCE_REQUIRES_PRO is enabled.
1. Signals ↔ Endpoints
| Signal | PolymarketAPIClient method | Endpoint / params | Derivation note |
|---|---|---|---|
trade_count | fetch_trades | GET /trades (market, asset_id, limit) | Count per wallet from maker/taker/user |
first_seen_ts (wallet) | fetch_activity | GET /activity (user, market, limit) | Min timestamp; or from baseline entity_type=wallet, metric=first_seen_ts |
volume | fetch_live_volume | GET /markets/{id}/volume or /volume | Stored in baseline entity_type=market, metric=volume |
open_interest | fetch_open_interest | GET /markets/{id}/open-interest or /open-interest | Stored in baseline entity_type=market, metric=open_interest |
leaderboard_rank | fetch_leaderboard | GET /leaderboard or GET /analytics/leaderboard | Optional; limit for top N |
holders_count | fetch_holders | GET /holders (token_id, limit) | Per outcome token |
outsized_bet (derived) | fetch_trades + baselines | — | Trade count vs baseline volume; alert when threshold exceeded |
new_wallet_anomaly (derived) | fetch_activity + get_wallet_first_activity_ts | — | New wallet with large activity |
low_activity_market_move (derived) | fetch_trades + fetch_live_volume | — | Price move with low historical volume |
2. Baseline schema
| Column | Type | Description |
|---|---|---|
entity_type | String(50) | "wallet", "market", "condition" |
entity_id | String(255) | Wallet address, market id, or condition id |
window | String(50) | "1d", "7d", "30d" |
metric | String(100) | "volume", "trade_count", "first_seen_ts", "open_interest", etc. |
value | JSONB | Numeric or object (e.g. timestamp, aggregate) |
computed_at | DateTime | When the baseline was last updated |
(entity_type, entity_id, window, metric) for upsert.
3. Alert schema
| Column | Type | Description |
|---|---|---|
alert_type | String(100) | "outsized_bet", "new_wallet_anomaly", "low_activity_market_move", etc. |
severity | String(20) | "low", "medium", "high", "critical" |
condition_id | String(255), nullable | Related condition/market |
proxy_wallet | String(66), nullable | Wallet involved |
event_id | String(255), nullable | Related event |
signal_values | JSONB, nullable | Snapshot of signals that fired |
message | Text | Human-readable description |
created_at | DateTime | When the alert was created |
reviewed_at | DateTime, nullable | When it was reviewed |
reviewed_by | Integer, FK users, nullable | Reviewer user id |
resolution | String(50), nullable | "dismissed", "escalated", "false_positive" |
4. Config
| Variable | Description | Default |
|---|---|---|
POLYMARKET_DATA_API_URL | Data API base (trades, activity, leaderboard, volume, open-interest) | https://data-api.polymarket.com |
POLYMARKET_SURVEILLANCE_ENABLED | Enable detection cycle (baselines, alerts) | false |
SURVEILLANCE_REQUIRES_PRO | Require Pro (or higher) for surveillance routes | true |
REVENUECAT_ENTITLEMENT_MARKET_INTELLIGENCE | Entitlement for surveillance; if unset, uses REVENUECAT_ENTITLEMENT_PRO | — |
5. Usage
Running the detection cycle
- Cron / scheduled: Call
POST /api/polymarket/surveillance/run-cycle(requires Pro andPOLYMARKET_SURVEILLANCE_ENABLED=true). Response:{ "skipped" }or{ "baselines_updated", "alerts_created" }. - Admin / UI: Use the same endpoint from an admin or scheduled job. The
PolymarketSurveillanceService.run_detection_cycle(markets=...)can be passed an optional list of market ids.
Listing and reviewing alerts
- List:
GET /api/polymarket/surveillance/alerts?severity=...&reviewed=...&limit=50&offset=0 - Review:
POST /api/polymarket/surveillance/alerts/{id}/reviewwith body{ "resolution": "dismissed" | "escalated" | "false_positive" }
SurveillanceAlertsPanel: it lists alerts, supports severity/reviewed filters, and a “Review” action with resolution dropdown. On 403 (subscription required), the panel shows an “Upgrade to Pro” CTA instead of the alerts table.