Developer Reference

EdgePro public API

Every endpoint that powers the dashboard is documented here. UK ASA-compliant — these endpoints don't return tips, only analytical signals you can interpret.

Engines

Per-system performance stats and the signal feed that powers the dashboard's engine tabs.

GET /api/systems

List of every tracked engine plus a summary stats block for each.

open
Query
// optional
since="2025-04-01T00:00:00Z"
Response
{
  "systems": ["ultimate_edge", "ew", "gh_lay"],
  "stats": {
    "ultimate_edge": { "tracked": 412, "win_rate": 22.4, "roi": 8.1 }
  }
}
GET /api/system-stats/<system>

Full stats block for one engine (tracked, wins, losses, ROI, win rate, last signal).

open
Response
{
  "system": "ultimate_edge",
  "tracked": 412,
  "settled": 398,
  "wins": 89,
  "losses": 309,
  "total_pnl": 142.50,
  "roi": 8.1,
  "win_rate": 22.4
}
GET /api/system-pnl/<system>

Cumulative P&L time-series for one engine, ready to plot.

open
Query
granularity="day"  // default
since="2025-04-01"  // optional
Response
{
  "system": "ew",
  "granularity": "day",
  "points": [
    { "date": "2025-05-01", "daily_pnl": 12.30, "pnl_cumulative": 12.30 }
  ]
}
GET /api/system-pnl-all

Bundled P&L series for every engine — one chart, one colour per system.

open
Response
{
  "granularity": "day",
  "series": {
    "ultimate_edge": [{ "date": "2025-05-01", "pnl_cumulative": 8.40 }],
    "gh_lay":         [{ "date": "2025-05-01", "pnl_cumulative": 3.10 }]
  }
}
GET /api/signals

Recent signal entries, newest first — feeds the per-system signal table.

open
Query
system="ew"          // optional filter
outcome="win"        // pending | win | place | lose | void
limit=200            // max 2000
Response
{
  "count": 2,
  "limit": 200,
  "entries": [
    { "system": "ew", "selection": "Yachtsman", "odds": 5.5, "outcome": "win", "profit_loss": 4.50 }
  ]
}

Transparency

ASA-compliant public view of every engine's tracked record — including the losing days.

GET /api/transparency/summary

Per-engine summary across rolling 30 / 60 / 90 / all-time windows.

open
Response
{
  "systems": ["ew", "gh_lay"],
  "stats": {
    "ew": {
      "tracked": 410, "pending": 3,
      "windows": {
        "30":  { "settled": 42, "roi": 9.1 },
        "all": { "settled": 395, "roi": 7.8 }
      }
    }
  },
  "disclaimer": "Past performance is not indicative of future results."
}
GET /api/transparency/timeseries/<system>

Cumulative P&L line over a rolling lookback. Use days=0 for all-time.

open
Query
days=90  // 30 | 60 | 90 | 365 | 0 (all-time)
Response
{
  "system": "ew", "days": 90, "granularity": "day",
  "points": [{ "date": "2025-03-01", "pnl_cumulative": 2.10 }]
}
GET /api/transparency/export/<system>.csv

CSV download of every settled signal for one engine. Columns: date, race, selection, odds, stake, outcome, profit_loss.

opentext/csv
GET /api/transparency/backtest

"What if I'd only taken bets in this odds range" — returns original and filtered curves to overlay.

open
Query
system="ew"          // required
min_odds=2.0
max_odds=10.0
days=365            // 0 = all-time
Response
{
  "system": "ew", "min_odds": 2.0, "max_odds": 10.0,
  "original": { "summary": { "n_settled": 395, "final": 142.50 } },
  "filtered": { "summary": { "n_settled": 180, "final": 98.40 } }
}

Bankroll

Per-user bankroll settings + Kelly-recommended stakes for today's active signals.

GET /api/bankroll/settings

Current bankroll, daily target, Kelly fraction, max stake cap.

session-scoped
Response
{
  "settings": {
    "bankroll": 1000.0,
    "daily_target_pct": 1.5,
    "kelly_fraction": 0.25,
    "max_stake_pct": 0.02
  }
}
POST /api/bankroll/settings

Update bankroll settings. Values are clamped to safe ranges server-side.

session-scoped
Body
{
  "bankroll": 1500,
  "daily_target_pct": 1.5,
  "kelly_fraction": 0.25,
  "max_stake_pct": 0.02
}
Response
{ "ok": true, "settings": { /* normalised */ } }
GET /api/bankroll/recommendations

Per-active-signal suggested stakes using fractional Kelly. Phrased as "suggested" — never an instruction to bet.

session-scoped
Response
{
  "total_signals": 4,
  "stakeable_signals": 3,
  "total_recommended_stake": 42.50,
  "items": [{
    "kind": "ultimate_edge", "horse": "Yachtsman", "odds": 5.5,
    "stake_recommendation": { "stake": 14.20, "edge_pct": 12.4, "verdict": "stake" }
  }]
}
GET /api/bankroll/summary

Today's P&L versus daily target, intraday drawdown, per-engine breakdown.

session-scoped
Response
{
  "target_gbp": 15.00,
  "progress_pct": 72.0,
  "intraday_pct": 1.08,
  "drawdown_warning": false,
  "today": { "today_pnl": 10.80, "today_wins": 2, "today_losses": 3 }
}

Pace Map

Per-race pace shape, draw bias, and runner-by-runner verdicts.

GET /api/pace-map/races

List of every race with a generated pace map (venue, off-time, pace shape).

open
Response
{
  "total": 28,
  "races": [{
    "idx": 0, "venue": "Ascot", "time": "14:10",
    "pace_shape": "LONE_LEADER", "field_size": 10
  }]
}
GET /api/pace-map/<idx>

One race's full pace map — runners with stall, running style, signal verdict, and the pace shape analysis.

open
Response
{
  "summary": { "venue": "Ascot", "pace_shape": "LONE_LEADER" },
  "runners": [{
    "horse": "Yachtsman", "stall": 4,
    "run_style": "LEADER", "combined_score": 82.5,
    "signal": "GOLDEN"
  }],
  "golden_picks": ["Yachtsman"],
  "traps": ["Slow Starter"]
}

Leaderboard

Recent settled-signal performance — top winners + per-engine summary over a chosen window.

GET /api/leaderboard/weekly

7-day leaderboard — alias for /api/leaderboard/7. Used by the landing-page widget.

open
Response
{
  "period_days": 7,
  "total_settled": 82, "total_pnl": 38.40,
  "overall_win_rate": 26.8,
  "per_engine": [{ "system": "ew", "pnl": 22.10, "roi": 11.2 }],
  "top_signals": [{ "selection": "Yachtsman", "odds": 5.5, "pnl": 9.0 }]
}
GET /api/leaderboard/<days>

Same shape as /weekly for any window. Path param is clamped to 1–365 days.

open

AI Chat

Free-text questions about a single UK race — answered by Claude with race-card context.

GET /api/race-chat/status

Is AI chat available? Which model? What rate limit applies?

open
Response
{
  "available": true,
  "model": "claude-haiku-4-5-20251001",
  "rate_limit": { "window_seconds": 3600, "max_questions": 10 }
}
GET /api/race-chat/races

Today's UK/IRE races available for chat (race_id, course, off_time).

open
Response
{
  "count": 28,
  "races": [{ "race_id": "rac_12345", "course": "Ascot", "off_time": "14:10" }]
}
POST /api/race-chat

Ask one question about one race. Returns analytical commentary — never a tip or instruction.

openrate-limited
Body
{
  "race_id": "rac_12345",
  "question": "Which runner has the strongest data case?",
  "session_id": "optional-for-rate-buckets"
}
Response
{
  "answer": "The data suggests **Yachtsman** has a strong case...",
  "tokens_used": 842,
  "model": "claude-haiku-4-5-20251001",
  "rate": { "used": 3, "cap": 10 }
}

Push

Web push (PWA) — VAPID-keyed browser notifications for high-EV signals.

GET /api/push/status

Is web push configured? How many subscriptions are active?

open
Response
{
  "available": true,
  "rate_limit_per_hour": 12,
  "active_subscriptions": 47,
  "vapid_public_present": true
}
GET /api/push/vapid-public-key

Base64url-encoded VAPID public key. Feed straight to PushManager.subscribe.

open
Response
{ "public_key": "BNc...wU" }
POST /api/push/subscribe

Store a browser push subscription. Pass the object returned by PushManager.subscribe.

open
Body
{
  "endpoint": "https://fcm.googleapis.com/fcm/send/...",
  "keys": { "p256dh": "BNc...", "auth": "xyz..." }
}
Response
{ "ok": true, "active_subscriptions": 48 }
POST /api/push/unsubscribe

Remove a subscription by endpoint.

open
Body
{ "endpoint": "https://fcm.googleapis.com/..." }
POST /api/push/test

Send a test "ping" notification to every active subscription.

session
Response
{ "sent": 47, "failed": 0, "dropped": 1 }

Email

Daily morning digest as a low-key alternative to push notifications.

GET /api/email-digest/status

Is the digest engine running? When does it fire? How many subscribers?

open
Response
{
  "available": true,
  "fire_hour_uk": 7,
  "active_subscriptions": 23,
  "from_addr": "digest@edgepro.local"
}
POST /api/email-digest/subscribe

Subscribe one email address to the daily digest.

open
Body
{ "email": "you@example.com" }
POST /api/email-digest/unsubscribe

Remove an email from the digest list.

open
Body
{ "email": "you@example.com" }
POST /api/email-digest/send-test

Send a one-off test digest to a single address. Requires an authenticated session — protects against open-relay abuse.

session required
Body
{ "email": "you@example.com" }

Ops

Operational endpoints — engine heartbeats and pre-launch readiness checks.

GET /api/health-monitor

Every engine's heartbeat freshness — categorised as healthy / stale / dead / unknown.

open
Query
max_age=600    // healthy/stale threshold (sec)
dead_age=3600  // stale/dead threshold (sec)
Response
{
  "counts": { "healthy": 9, "stale": 1, "dead": 0, "unknown": 0 },
  "engines": [{
    "name": "Each-Way Value", "category": "healthy",
    "age_seconds": 42.1, "status": "ok"
  }]
}
GET /api/launch-ready

Pre-launch readiness report — .env, deps, engine compile, heartbeats, data integrity. Pass ?deep=1 to also probe external APIs.

open
Response
{
  "overall": "PASS",
  "blocker_count": 0,
  "totals": { "pass": 38, "warn": 4, "fail": 0, "skip": 1 },
  "sections": { /* one entry per check group */ }
}

No endpoints match your search.