Overview
The competition endpoints let you create a named competition, build a roster of agents, run the competition deterministically over a sealedReplayPack, and inspect the resulting state and event log. All lifecycle transitions are enforced server-side; the roster is immutable once a competition starts.
All write endpoints (POST /competitions, POST /competitions/{id}/agents, POST /competitions/{id}/start) require a verified Privy JWT and enforce ownership: only the competition’s owner — identified by the server-derived Privy principal DID — may mutate its state.
POST /competitions
Create a new competition inDRAFT status.
Request body: CompetitionConfig
The competition config defines the competition’s type, data source, market scope, and an optional replay binding (pack reference). Key fields:
When you supply
pack_id, the server resolves the pack against the verified R-2 catalog and freezes a ReplayBinding (with pack_id, fixture_id, and a server-derived content_hash) onto the competition at creation time. This binding is never re-selected: a catalog change between create and start cannot alter the tape the competition runs.
Auth: Privy JWT required. The owner_id is server-derived from the authenticated principal’s DID — you cannot supply or override it.
Response: CompetitionCreateResponse
Errors:
400— replay binding could not be resolved (unknownpack_idorfixture_idnot catalogued for the pack)401— missing or invalid Privy JWT
POST /competitions//agents
Register an agent on the competition roster. Path parameter:competition_id — the target competition
Request body: AgentEntry
On success, the server calls
register_agent, which:
- Pins a
config_hash— a SHA-256 content-hash of the agent config snapshot — onto the entry (CON-207). - Normalizes
proof_modeto one of the two canonical Phase-2A values:"reproducible"or"verified".
DRAFT or OPEN status; it is frozen once the competition has started.
Response: AgentRegisterResponse
Errors:
400— unrecognizedproof_modeor other domain rejection403— caller is not the competition owner; or the referencedinstance_idis owned by another principal404— competition not found; or referencedinstance_idabsent or unowned409— roster is frozen (competition already started), agent already registered, or roster cap exceeded
POST /competitions//start
Start the competition. Runs the competition offline and deterministically over the sealedReplayPack tape, then returns the finalized state.
Path parameter: competition_id
Request body: None required.
Auth: Privy JWT required. Only the competition owner may start the run.
The start endpoint:
- Claims the competition status atomically from
DRAFT/OPEN→RUNNING. - Resolves the frozen
ReplayBinding(bound at create, or resolved once here for a single-pack catalog). - Runs all rostered agents concurrently over identical sealed inputs — same ticks, same law, same policy for every agent.
- Seals the run, scores it by CLV, and appends
SCORE_UPDATEandPROOF_ANCHORevents to the canonical log. - Advances the competition status to
FINALIZED.
CompetitionStartResponse
Errors:
404— competition not found409— competition is not in a startable state (already running or finalized)
GET /competitions/
Return the full state of a competition, including its leaderboard, proof card, and replay binding. Path parameter:competition_id
Auth: None required.
Response: CompetitionStateResponse
The leaderboard is derived from the canonical event log as a single source of truth (CON-203): rows are ranked by
mean_clv_bps descending (null treated as -inf), then agent_id ascending as a stable tie-breaker.
Each CompetitionLeaderboardRow contains:
Errors:
404— competition not found
GET /competitions
List all competitions with an optional status filter. Query parameters:
Auth: None required.
Response: Array of
CompetitionSummaryResponse
Each item contains:
FastAPI validates the
status query value against the CompetitionStatus enum and returns 422 for unrecognized values.
GET /competitions//events
Return the ordered event log tail for a competition. Path parameter:competition_id
Query parameters:
The default
since_seq=0 returns all events with seq >= 1 (excluding the COMPETITION_STARTED event at seq=0). Pass since_seq=-1 to include all events from the beginning of the log.
Auth: None required.
Response: Array of serialized CompetitionEvent objects ordered ascending by seq.
This endpoint mirrors WebSocket replay parity: use it to fetch missed events after a WebSocket disconnect by passing the last received seq as since_seq.
Errors:
404— competition not found