Judging
The judging surface covers two organizer concerns: defining the evaluation rubric
(criteria) and assigning judges to the teams they evaluate. Score submission stays
a judge-side action - see the note at the end. Success responses use the standard
{ "success": true, "data": ... } envelope.
Read
List judges GET /events/:eventId/judges
{ "data": { "judges": [ { "user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "name": "Mei Tanaka", "email": "mei.tanaka@example.com" } ], "count": 1 }} List criteria GET /events/:eventId/criteria
{ "data": { "criteria": [ { "id": "3a5c7e91-2b4d-4f60-8c19-7d2e4a6b8c03", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "name": "Innovation", "description": "How novel is the solution", "weight": 0.4, "order_index": 0 } ], "count": 1 }} List assignments GET /events/:eventId/assignments
{ "data": { "assignments": [ { "id": "6b8d0f24-5a3c-4e19-b7f2-8c0a2e4d6b91", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "judge_user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "status": "PENDING", "progress": 0, "judge_name": "Mei Tanaka", "judge_email": "mei.tanaka@example.com", "team_name": "Beat Wizards", "created_at": "2026-07-14T09:00:00.000Z" } ], "count": 1 }} Get assignment GET /assignments/:id
{ "data": { "id": "6b8d0f24-5a3c-4e19-b7f2-8c0a2e4d6b91", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "judge_user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "status": "PENDING", "progress": 0, "judge_name": "Mei Tanaka", "judge_email": "mei.tanaka@example.com", "team_name": "Beat Wizards", "team_problem": "Cut support response time in half with an agent", "event_name": "Autonomous Agents Track", "event_slug": "autonomous-agents", "super_event_scoring_mode": "slider" }} Get assignment scores GET /assignments/:id/scores
{ "data": { "scores": [ { "id": "e7a9c1b3-4d6f-4082-9a51-2c4e6b8d0f37", "assignment_id": "6b8d0f24-5a3c-4e19-b7f2-8c0a2e4d6b91", "criterion_id": "3a5c7e91-2b4d-4f60-8c19-7d2e4a6b8c03", "value": 4, "comment": "Strong technical execution, would love deeper market validation", "feedback": "Great work on the agent orchestration layer", "submitted_at": "2026-07-14T15:30:00.000Z", "criterion_name": "Innovation", "criterion_weight": 0.4 } ], "count": 1 }} Get results GET /events/:eventId/results
{ "data": { "results": [ { "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "team_name": "Beat Wizards", "overall_score": 4.1234, "rank": 1, "created_at": "2026-07-14T16:00:00.000Z" } ], "count": 1, "notifications_sent_at": null }} Get team result GET /teams/:teamId/results
{ "data": { "overall_score": 4.1234, "rank": 1, "created_at": "2026-07-14T16:00:00.000Z", "score_breakdown": [ { "criterion_name": "Innovation", "criterion_description": "How novel is the solution", "score_value": 4, "feedback": null } ] }}Per-criterion breakdown shows participant-visible feedback only (the internal
judge comment is excluded).
Get judge progress GET /events/:eventId/judge-progress
{ "data": { "overview": { "total_assignments": 10, "total_judges": 3, "pending_assignments": 4, "submitted_assignments": 2, "ready_assignments": 4, "overall_progress": 65 }, "judges": [ { "judge_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "judge_name": "Mei Tanaka", "judge_email": "mei.tanaka@example.com", "total_assignments": 4, "pending_count": 1, "submitted_count": 1, "ready_count": 2, "overall_progress": 75 } ] }} Export results (CSV) GET /events/:eventId/results/csv
Returns CSV text (Content-Type text/csv), not JSON. Columns:
Rank, Team Name, Overall Score, Calculated At Export scores (CSV) GET /events/:eventId/scores/csv
Returns CSV text (Content-Type text/csv), not JSON. Columns:
Team Name, Judge Name, Average Score, Judge EmailFollowed by two columns per criterion: "<criterion> - Score", "<criterion> - Comment".
Write
Create criterion POST /events/:eventId/criteria
{ "data": { "id": "3a5c7e91-2b4d-4f60-8c19-7d2e4a6b8c03", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "name": "Innovation", "description": "How novel is the solution", "weight": 0.4, "order_index": 0 }}{ "name": "Innovation", "description": "How novel is the solution", "weight": 0.4, "order_index": 0}| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | |
weight | number | Yes | Between 0 and 1. The sum of all criteria weights on a track cannot exceed 1.0. |
order_index | number | Yes | Display order. |
description | string | No |
Returns 201. weight is between 0 and 1; the sum of all criteria weights on a
track cannot exceed 1.0 - the API rejects creates that would push the total over.
Update criterion PATCH /criteria/:id
{ "data": { "id": "3a5c7e91-2b4d-4f60-8c19-7d2e4a6b8c03", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "name": "Innovation", "description": "How novel is the solution", "weight": 0.25, "order_index": 0 }}{ "name": "Innovation", "description": "How novel is the solution", "weight": 0.25, "order_index": 1}All fields optional - send any subset.
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | No | |
description | string | No | |
weight | number | No | Between 0 and 1; total across criteria cannot exceed 1.0. |
order_index | number | No |
Delete criterion DELETE /criteria/:id
{ "data": { "deleted": true }} Clone criteria POST /events/:eventId/criteria/clone/:sourceEventId
{ "data": { "criteria": [ { "id": "3a5c7e91-2b4d-4f60-8c19-7d2e4a6b8c03", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "name": "Innovation", "description": "How novel is the solution", "weight": 0.4, "order_index": 0 } ], "count": 1 }}Copy all criteria from one track to another - handy for a template track holding the canonical rubric.
Assign judge POST /events/:eventId/assignments
{ "data": { "id": "6b8d0f24-5a3c-4e19-b7f2-8c0a2e4d6b91", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "judge_user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "status": "PENDING", "progress": 0, "created_at": "2026-07-14T09:00:00.000Z" }}{ "judge_user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53"}| Field | Type | Required | Notes |
|---|---|---|---|
judge_user_id | string (UUID) | Yes | Must hold the JUDGE role on this track. |
team_id | string (UUID) | Yes | Must be a team in this track. |
Bulk-assign judges POST /events/:eventId/assignments/bulk
{ "data": { "assignments": [ { "id": "6b8d0f24-5a3c-4e19-b7f2-8c0a2e4d6b91", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "judge_user_id": "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "status": "PENDING", "progress": 0, "created_at": "2026-07-14T09:00:00.000Z" } ], "created": 1, "skipped": 0 }}If every requested judge was already assigned, returns 200:
{ "data": { "created": 0, "skipped": 2, "message": "All judges are already assigned" }}{ "judge_user_ids": [ "d5f0a3b2-6e7c-4a1d-8f39-5b8c2e4d7a16", "f8b1d3a5-7c9e-4021-8b46-1d3f5a7c9e08", "a2c4e6f8-1b3d-4570-9e82-4c6a8b0d2f19" ], "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53"}| Field | Type | Required | Notes |
|---|---|---|---|
judge_user_ids | string[] | Yes | Non-empty array; all must hold the JUDGE role on this track. |
team_id | string (UUID) | Yes | Must be a team in this track. |
Assign N judges to one team - the primary endpoint for the “judges per challenge” workflow. The user IDs must have JUDGE role on this track; already-assigned judges are skipped.
Unassign judge DELETE /assignments/:id
{ "data": { "deleted": true }}Cascade-deletes any scores the judge had submitted under that assignment.
Calculate results POST /events/:eventId/calculate-results
{ "data": { "results": [ { "id": "c3e5a7b9-1d2f-4860-9a04-3c5e7a9b1d26", "event_id": "5c1f8a73-6d2b-4e90-a1c4-7b8e3f0d2a16", "team_id": "7e3a0c92-1b5d-4f86-9c20-2d4a6b8e1f53", "overall_score": 4.1234, "rank": 1, "created_at": "2026-07-14T16:00:00.000Z", "updated_at": "2026-07-14T16:00:00.000Z", "team_name": "Beat Wizards" } ], "count": 1 }}Returns 201. Aggregates submitted scores against criteria weights to produce the
final leaderboard. Teams with no scores are skipped.
Score submission is not in this scope
POST /assignments/:id/scores, PATCH /scores/:id, and POST /assignments/:id/submit
are judge-side actions - they need a real authenticated judge session, not a service
integration. If you need to bulk-import scores, contact the BuilderBase team to
scope a dedicated scores:write permission.