BoardSim — Frontend API Specification
Overview
The frontend communicates with the backend via REST/HTTP or WebSocket endpoints. The backend is a FastAPI server running at a configurable base URL (default: http://localhost:8000 for local dev, or https://<USER>-board-sim-env.hf.space for production).
Key Principle: Frontend and backend are fully decoupled. The frontend only needs to know these endpoints; it does not import any backend code.
1. REST Endpoints
POST /reset
Purpose: Start a new game episode.
Request Body:
{
"seed": 42,
"episode_id": "optional-uuid-string"
}
Response (200 OK):
{
"observation": {
"state": {
"round": 1,
"revenue": 2000000.0,
"burn_rate": 1200000.0,
"runway_months": 14.0,
"product_readiness": 0.45,
"market_share": 0.08,
"team_morale": 0.70,
"investor_confidence": 0.65,
"regulatory_risk": 0.20,
"profitability_score": 0.0,
"trust": {
"CTO": 0.5,
"CFO": 0.5,
"Investor Rep": 0.5,
"Independent": 0.5
},
"trust_history": [
{
"round": 0,
"CTO": 0.5,
"CFO": 0.5,
"Investor Rep": 0.5,
"Independent": 0.5
}
],
"history": [],
"done_reason": null,
"winning_decision": null
},
"event": "New Competitor Entry — A larger competitor enters your core market with aggressive pricing and threatens your customer base.",
"options": [
"cut_prices",
"double_down_on_quality",
"form_strategic_partnership"
],
"npc_statements": [
{
"role": "CTO",
"statement": "From an operational standpoint, the trade-offs here are clear. I'm voting double_down_on_quality.",
"vote": "double_down_on_quality",
"confidence": 0.78
},
{
"role": "CFO",
"statement": "From a fiduciary standpoint, only one of these is defensible. I'm voting cut_prices.",
"vote": "cut_prices",
"confidence": 0.66
},
{
"role": "Investor Rep",
"statement": "We were not funded to play it safe. I'm voting form_strategic_partnership.",
"vote": "form_strategic_partnership",
"confidence": 0.70
},
{
"role": "Independent",
"statement": "Long-term reputation outlasts any single quarter. I'm voting double_down_on_quality.",
"vote": "double_down_on_quality",
"confidence": 0.59
}
],
"round": 1
},
"done": false,
"info": {
"episode_id": "uuid-string",
"seed": 42
}
}
POST /step
Purpose: Submit the agent's decision for the current round.
Request Body:
{
"action": {
"decision": "double_down_on_quality",
"coalition_pitch": "Investing in product quality protects long-term reputation and reduces operational risk while preserving margin discipline."
}
}
Response (200 OK):
{
"observation": {
"state": {
"round": 2,
"revenue": 2000000.0,
"burn_rate": 900000.0,
"runway_months": 18.5,
"product_readiness": 0.45,
"market_share": 0.08,
"team_morale": 0.65,
"investor_confidence": 0.60,
"regulatory_risk": 0.20,
"profitability_score": 12.34,
"trust": {
"CTO": 0.65,
"CFO": 0.70,
"Investor Rep": 0.40,
"Independent": 0.55
},
"trust_history": [
{
"round": 0,
"CTO": 0.5,
"CFO": 0.5,
"Investor Rep": 0.5,
"Independent": 0.5
},
{
"round": 1,
"CTO": 0.65,
"CFO": 0.70,
"Investor Rep": 0.40,
"Independent": 0.55
}
],
"history": [
{
"round": 1,
"event_title": "Round 1 — Series-B runway crunch",
"agent_decision": "cut_costs",
"winning_decision": "cut_costs",
"reward": 1.25,
"profitability_before": 0.0,
"profitability_after": 12.34
}
],
"done_reason": null,
"winning_decision": "cut_costs"
},
"event": "Round 2 — Enterprise contract w/ source-code escrow\nDescription: A Fortune 500 enterprise wants to sign a $5M contract but demands source code escrow.",
"options": [
"accept_deal",
"negotiate_terms",
"reject_deal"
],
"npc_statements": [
{
"role": "CTO",
"statement": "...",
"vote": "...",
"confidence": 0.XX
}
],
"round": 2
},
"reward": 1.25,
"done": false,
"info": {
"round": 2,
"winning_decision": "cut_costs",
"winning_vote_tally": {
"cut_costs": 4.2,
"raise_capital": 1.3,
"reduce_scope": 0.5
},
"pitch_scores": {
"CTO": 0.0,
"CFO": 0.0,
"Investor Rep": 0.0,
"Independent": 0.0
}
}
}
GET /health
Purpose: Health check. Confirms backend is running.
Response (200 OK):
{
"status": "healthy"
}
GET /docs
Purpose: Auto-generated Swagger/OpenAPI documentation. Use for development reference.
Location: http://localhost:8000/docs (or on HF Space at /docs)
2. WebSocket Streaming (Optional, Advanced)
If you want real-time streaming during training or multi-agent play:
WebSocket /ws
Purpose: Bi-directional message streaming (not required for single-agent frontend).
Connection example:
const ws = new WebSocket("ws://localhost:8000/ws");
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(message); // e.g., { "type": "step", "observation": {...} }
};
(Details omitted if not used for initial frontend.)
3. Data Models Reference
BoardSimObservation (returned by /reset and /step)
{
state: {
round: number, // 1-indexed: 1..10
revenue: number, // in dollars
burn_rate: number, // monthly spend in dollars
runway_months: number, // months until bankruptcy
product_readiness: float (0..1),
market_share: float (0..1),
team_morale: float (0..1),
investor_confidence: float (0..1),
regulatory_risk: float (0..1),
profitability_score: number,
trust: { // per NPC, 0..1
"CTO": 0.5,
"CFO": 0.5,
"Investor Rep": 0.5,
"Independent": 0.5
},
trust_history: Array, // per-round trust snapshots
history: Array, // past decisions & outcomes
done_reason: string | null, // e.g., "bankruptcy", "acquisition", "ipo", null
winning_decision: string | null
},
event: string, // event title + description
options: [string, string, string], // 3 valid decision strings for this round
npc_statements: [
{
role: "CTO" | "CFO" | "Investor Rep" | "Independent",
statement: string,
vote: string (one of options),
confidence: float (0..1)
},
// ... one per NPC role (4 total)
],
round: number
}
BoardSimAction (sent to /step)
{
decision: string, // must be one of observation.options
coalition_pitch: string | null // optional persuasion attempt (unused in v1)
}
4. Error Responses
422 Unprocessable Entity
Invalid action format or decision not in options.
Response:
{
"detail": [
{
"loc": ["body", "action", "decision"],
"msg": "value is not a valid enumeration member",
"type": "type_error.enum"
}
]
}
400 Bad Request
Malformed JSON or missing required fields.
5. Frontend Integration Checklist
- Initialize: On app load, call
POST /resetto get initial observation. - Display State: Render
observation.stateas metrics (revenue, runway, morale, trust, etc.). - Display Event: Show
observation.event(crisis title + description). - Display NPCs: Render 4 NPC cards with their
statement,vote, andconfidence. - Render Decision Options: Display 3 buttons (or cards) for each string in
observation.options. - Handle User Click: On decision click, POST
/stepwith the selecteddecision. - Update UI: Parse response observation and repeat from "Display State".
- Terminal State: If
doneis true, show final metrics anddone_reason(e.g., "Bankruptcy", "IPO"). - Optional Coalition Pitch: Text input for
coalition_pitch(future extension; safe to leave blank for v1).
6. Backend Base URL Configuration
For local development:
http://localhost:8000
For HF Space deployment (after openenv push):
https://<your-hf-username>-board-sim-env.hf.space
Frontend environment variable (optional):
REACT_APP_API_BASE_URL=http://localhost:8000
// or
REACT_APP_API_BASE_URL=https://<your-hf-username>-board-sim-env.hf.space
7. Example Frontend Workflow
// 1. Reset
const resetRes = await fetch(`${API_BASE}/reset`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ seed: 42 })
});
const { observation, done, info } = await resetRes.json();
// 2. Render observation
displayState(observation.state);
displayNPCStatements(observation.npc_statements);
displayDecisionButtons(observation.options);
// 3. User clicks decision
const decision = "cut_costs"; // from button click
const stepRes = await fetch(`${API_BASE}/step`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
action: { decision, coalition_pitch: "" }
})
});
const { observation: nextObs, reward, done: nextDone } = await stepRes.json();
// 4. Repeat or show results
if (nextDone) {
displayEndgameScreen(nextObs.state, nextObs.state.done_reason);
} else {
displayState(nextObs.state);
// ... repeat
}
8. No Backend Imports in Frontend
✅ OK: fetch("http://localhost:8000/reset")
❌ NOT OK: import { BoardSimEnvironment } from "backend"
The frontend is a standalone web app. All communication is via HTTP/WebSocket.