User-configurable preset and screen panels on /dashboard2/market-watch — Upstash-cached, per-tier rate-limited dashboard surface.
Market Watch panels are user-configured surfaces that live above the fixed “Radar pulse” panes on /dashboard2/market-watch. Each panel is either a preset (one of eight pre-built fetchers) or a screen (filter expressed over a single read-only source).
These endpoints are session-authenticated only (Supabase cookie), not Bearer-API. They power the dashboard UI; external automation should use the public/agent APIs.
The system is gated three ways:
Per-route rate limit — same withRequestLog wrapper as the rest of the dashboard. RPM + monthly hard cap per tier.
Tier panel caps — number of panels you can keep is market_watch_panel_cap from tier_config. Screen panels have a tighter cap (market_watch_screen_panel_cap).
Per-panel refresh floor — server clamps any schedule.cadenceMinutes to market_watch_min_refresh_seconds and a manual refresh has its own per-panel cooldown.
Returns the legacy fixed-pane payload, every active panel for the calling user (with its cached payload), and the current tier limits + counts. Response is private, max-age=60, stale-while-revalidate=300.
Panel fan-out is bounded at pMap(concurrency=4) so even an institutional tier with 200 panels only runs 4 factory calls at a time. Cache hits never reach Postgres.
PATCH body accepts any subset of { title, spec, status, sortIdx }. Spec PATCH replaces the full spec — no JSON merge. Cache for the panel is invalidated after commit.Reorder body: { "order": [{ "id": "...", "sortIdx": 0 }, ...] }. All ids must be owned by the caller, else 404 panel_not_found_or_not_owned.
Redis outage — runPanelWithCache falls through to bypass (factory runs inline, no cache write). Cooldown bypass is graceful too: the panel’s refresh_floor_seconds floor still gates the cache window once Redis recovers.
Validator failure — never persists. The reason enum is stable and safe to surface.
Factory error — recorded in market_watch_panel_runs.error plus market_watch_panels.last_error. Replays still work via the next refresh.