> ## Documentation Index
> Fetch the complete documentation index at: https://dev-doc.dualmindlab.tech/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the DualMind developer and admin docs. The API base URL is https://api.dualmindlab.tech and the local API runs on http://localhost:5079.
> Never send POST, PUT, PATCH or DELETE requests to the production API while developing; use a local API and a development Supabase project.
> Environment variables are documented by name only. Never ask for or print secret values.
> Admin routes under /api/admin require a Supabase access token for a user whose public.users.role is admin. The PUT routes for users, models, providers and threads write every field, so send the full record; PUT /api/admin/keys/{keyId} writes only the fields you send (sending api_key replaces the key).

# Status and health checks

> Live API status from your browser, the health endpoints and what they tell you, and how to watch the service.

export const LiveLeaderboard = ({limit = 10, endpoint = "https://api.dualmindlab.tech/api/arena/model-stats"}) => {
  const [items, setItems] = useState(null);
  const [error, setError] = useState(null);
  const [provider, setProvider] = useState("all");
  const [minBattles, setMinBattles] = useState(50);
  const current = useRef(null);
  const load = useCallback(async () => {
    if (current.current) current.current.abort();
    const ctrl = new AbortController();
    current.current = ctrl;
    setError(null);
    setItems(null);
    let timedOut = false;
    const timer = setTimeout(() => {
      timedOut = true;
      ctrl.abort();
    }, 15000);
    try {
      const res = await fetch(endpoint, {
        signal: ctrl.signal
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const body = await res.json();
      if (current.current !== ctrl) return;
      setItems(Array.isArray(body.items) ? body.items : []);
    } catch (err) {
      if (current.current !== ctrl) return;
      setError(timedOut ? "The request timed out." : "Couldn't reach the API.");
    } finally {
      clearTimeout(timer);
    }
  }, [endpoint]);
  useEffect(() => {
    load();
    return () => {
      if (current.current) current.current.abort();
    };
  }, [load]);
  const providers = {
    groq: "Groq",
    google: "Google",
    cloudflare: "Cloudflare"
  };
  const rows = useMemo(() => {
    if (!items) return [];
    return items.filter(m => provider === "all" || m.providerName === provider).filter(m => (m.totalResponses || 0) >= minBattles).sort((a, b) => (b.eloScore || 0) - (a.eloScore || 0)).slice(0, limit);
  }, [items, provider, minBattles, limit]);
  const [lo, hi] = useMemo(() => {
    if (!rows.length) return [900, 1200];
    const scores = rows.map(r => r.eloScore || 0);
    return [Math.min(...scores) - 20, Math.max(...scores)];
  }, [rows]);
  return <div className="dm-widget not-prose" role="group" aria-label="Live leaderboard">
      <div className="dm-widget-head">
        <div>
          <span className="dm-label">Live data</span>
          <p className="dm-widget-title">Arena leaderboard</p>
        </div>
        <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.5rem"
  }}>
          <select className="dm-ctl" value={provider} onChange={e => setProvider(e.target.value)} aria-label="Filter by provider">
              <option value="all">All providers</option>
              <option value="groq">Groq</option>
              <option value="google">Google AI Studio</option>
              <option value="cloudflare">Cloudflare Workers AI</option>
            </select>
          <select className="dm-ctl" value={minBattles} onChange={e => setMinBattles(Number(e.target.value))} aria-label="Minimum number of battles">
              <option value={0}>Any battles</option>
              <option value={20}>20+ battles</option>
              <option value={50}>50+ battles</option>
              <option value={100}>100+ battles</option>
            </select>
          <button type="button" className="dm-ctl" onClick={load}>
            Refresh
          </button>
        </div>
      </div>

      {error && <p role="alert" style={{
    margin: 0
  }}>
          {error} Try again, or call <code>GET /api/arena/model-stats</code> yourself.
        </p>}

      {!error && !items && <div aria-busy="true" aria-label="Loading leaderboard" style={{
    display: "grid",
    gap: "0.7rem"
  }}>
          {[0, 1, 2, 3, 4].map(i => <div key={i} className="dm-skeleton" style={{
    width: `${92 - i * 9}%`
  }} />)}
        </div>}

      {!error && items && <div className="dm-table-wrap">
          <table className="dm-table">
            <caption className="dm-muted" style={{
    fontSize: "0.8rem"
  }}>
              Top {rows.length} of {items.length} ranked models, by ELO. Rank is the overall rank.
            </caption>
            <thead>
              <tr>
                <th scope="col">Rank</th>
                <th scope="col">Model</th>
                <th scope="col">ELO</th>
                <th scope="col" style={{
    textAlign: "right"
  }}>Win rate</th>
              </tr>
            </thead>
            <tbody>
              {rows.map(m => <tr key={m.modelId || m.modelName}>
                  <td>#{m.eloRank}</td>
                  <td style={{
    maxWidth: "16rem"
  }}>
                    <span style={{
    fontWeight: 600,
    overflowWrap: "anywhere"
  }}>{m.displayName || m.modelName}</span>
                    <div className="dm-muted" style={{
    fontSize: "0.75rem"
  }}>
                      {providers[m.providerName] || m.providerName} · {m.totalResponses} battles · {m.totalWins}W {m.totalLosses}L {m.totalTies}T
                    </div>
                  </td>
                  <td style={{
    minWidth: "8rem"
  }}>
                    <div style={{
    display: "flex",
    alignItems: "center",
    gap: "0.5rem"
  }}>
                      <span>{Math.round(m.eloScore)}</span>
                      <div className="dm-bar" aria-hidden="true" style={{
    flex: 1
  }}>
                        <span style={{
    width: `${Math.max(6, (m.eloScore - lo) / Math.max(1, hi - lo) * 100)}%`
  }} />
                      </div>
                    </div>
                  </td>
                  <td style={{
    textAlign: "right"
  }}>{Number(m.winRate || 0).toFixed(1)}%</td>
                </tr>)}
              {rows.length === 0 && <tr>
                  <td colSpan={4} className="dm-muted">
                    No models match these filters.
                  </td>
                </tr>}
            </tbody>
          </table>
        </div>}
    </div>;
};

export const ApiStatus = ({endpoint = "https://api.dualmindlab.tech/health", title = "DualMind API"}) => {
  const [state, setState] = useState({
    status: "checking"
  });
  const check = useCallback(async () => {
    setState(s => ({
      ...s,
      status: "checking"
    }));
    const started = performance.now();
    const ctrl = new AbortController();
    const timer = setTimeout(() => ctrl.abort(), 10000);
    try {
      const res = await fetch(endpoint, {
        signal: ctrl.signal,
        cache: "no-store"
      });
      const ms = Math.round(performance.now() - started);
      const body = await res.json().catch(() => ({}));
      const healthy = res.ok && body.status === "healthy";
      setState({
        status: healthy ? "up" : "degraded",
        ms,
        code: res.status,
        version: body.version,
        instance: typeof body.instanceId === "string" ? body.instanceId.slice(0, 8) : undefined,
        checkedAt: new Date()
      });
    } catch (err) {
      setState({
        status: "down",
        checkedAt: new Date()
      });
    } finally {
      clearTimeout(timer);
    }
  }, [endpoint]);
  useEffect(() => {
    check();
  }, [check]);
  const label = ({
    checking: "Checking…",
    up: "Operational",
    degraded: "Degraded",
    down: "Unreachable"
  })[state.status];
  const dot = ({
    checking: "",
    up: "dm-dot-up",
    degraded: "dm-dot-warn",
    down: "dm-dot-down"
  })[state.status];
  return <div className="dm-widget not-prose" role="group" aria-label={`${title} status`}>
      <div className="dm-widget-head">
        <div>
          <span className="dm-label">Live status</span>
          <p className="dm-widget-title">{title}</p>
        </div>
        <button type="button" className="dm-ctl" onClick={check} disabled={state.status === "checking"}>
          Check again
        </button>
      </div>
      <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.6rem",
    alignItems: "center"
  }} aria-live="polite">
        <span className="dm-pill">
          <span className={`dm-dot ${dot}`} aria-hidden="true" />
          {label}
        </span>
        {state.ms !== undefined && <span className="dm-pill">{state.ms} ms round trip</span>}
        {state.version && <span className="dm-pill">v{state.version}</span>}
        {state.instance && <span className="dm-pill dm-hide-sm">instance {state.instance}</span>}
      </div>
      <p className="dm-muted" style={{
    margin: "0.85rem 0 0",
    fontSize: "0.8rem"
  }}>
        {state.checkedAt ? `Checked from your browser at ${state.checkedAt.toLocaleTimeString()} with GET ${endpoint.replace("https://", "")}.` : `Calling GET ${endpoint.replace("https://", "")} from your browser…`}
      </p>
    </div>;
};

## Right now

<ApiStatus />

The check above runs in your browser with a read-only `GET https://api.dualmindlab.tech/health`. If it fails here but works with `curl`, suspect your network or an extension first.

## Health endpoints

| Endpoint | Auth | Returns | Use it for |
| :- | :- | :- | :- |
| `GET /health` | None | `status`, `message`, `version`, `timestamp`, `instanceId` | Liveness, the cron keep-alive and the deploy check |
| `GET /api/health` | None | Same as `/health` | Clients that only proxy `/api/*` |
| `GET /api/arena/ping` | None | `success`, `message`, `version` | A route through the arena controller |
| `GET /api/admin/dashboard/health` | AdminOnly | `status`, `supabase_connected` | Confirms the API can reach the database (and that you're an admin) |
| `GET /api/arena/model-stats` | None | Leaderboard rows | A read through Supabase and the `v_leaderboard` view |

<Info>
  `/health` answers from the container without touching Supabase or any provider. A healthy `/health` with failing battles points at configuration (Supabase keys, AI Gateway) rather than hosting. See [Troubleshooting](/development/troubleshooting).
</Info>

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -s https://api.dualmindlab.tech/health
  ```

  ```javascript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  const res = await fetch("https://api.dualmindlab.tech/health", { cache: "no-store" });
  const { status, version, instanceId } = await res.json();
  console.log(status, version, instanceId);
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import requests

  health = requests.get("https://api.dualmindlab.tech/health", timeout=10).json()
  print(health["status"], health["version"], health["instanceId"])
  ```
</CodeGroup>

```json Response theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "status": "healthy",
  "message": "DualMind API is running",
  "timestamp": "2026-10-17T19:54:46.7135698Z",
  "version": "1.0.0",
  "instanceId": "67c5fc5b5352c545…"
}
```

`instanceId` is the Durable Object ID of the container that answered (`local` when you run the API yourself). With one container it stays the same until the next deploy or restart.

## A live read through the database

The leaderboard below is fetched from your browser with `GET https://api.dualmindlab.tech/api/arena/model-stats`. If it loads, the API, Supabase and the `v_leaderboard` view are all working.

<LiveLeaderboard limit={5} />

## Watching the service

<Steps>
  <Step title="Keep-alive">
    The `dualmind-api` Worker's cron pings `/health` every five minutes; its log line is `[keep-alive] Container health: 200`.
  </Step>

  <Step title="Logs">
    `npx wrangler tail dualmind-api` streams Worker and container logs. Every request logs a start and end line with its correlation ID, status and duration.
  </Step>

  <Step title="After a deploy">
    CI polls `/health` up to five times. Then run the [release checklist](/deployment/checklist).
  </Step>
</Steps>

<Note>
  There is no public status page or external uptime monitor wired up yet. Adding one (and alerts on error rates) is on the [roadmap](/roadmap).
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.