Skip to content

API Reference

REST API for the NextJudge data layer.

Base URL: http://localhost:5000 (dev). Routes under /v1/ unless noted.

Auth first: Authentication. TL;DR: raw JWT in Authorization header, no Bearer.

Terminal window
# 1. Register
RESP=$(curl -s -X POST http://localhost:5000/v1/basic_register \
-H "Content-Type: application/json" \
-d '{"name":"ada","email":"ada@example.com","password":"example-password"}')
TOKEN=$(echo $RESP | jq -r .token)
USER_ID=$(echo $RESP | jq -r .id)
# 2. Languages (no auth)
LANG_ID=$(curl -s http://localhost:5000/v1/languages | jq -r '.[] | select(.name=="python") | .id')
# 3. Submit
SUB_ID=$(curl -s -X POST http://localhost:5000/v1/submissions \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"user_id\":\"$USER_ID\",\"problem_id\":1,\"language_id\":\"$LANG_ID\",\"source_code\":\"print(1)\"}" \
| jq -r .id)
# 4. Poll
curl -s http://localhost:5000/v1/submissions/$SUB_ID -H "Authorization: $TOKEN" | jq .status

MethodPathAuth headerPurpose
POST/v1/basic_registernoneCreate account + JWT
POST/v1/basic_loginnoneLogin + JWT
POST/v1/create_or_login_userAUTH_PROVIDER_PASSWORDOAuth bridge (web app)
POST/v1/login_judgeJUDGE_PASSWORDJudge worker JWT
POST/v1/basic_request_password_resetnoneRequest reset
POST/v1/basic_reset_passwordnoneSet new password

See Authentication for bodies and responses.


List users. Query: ?username= (filter by name).

Auth: admin

[{ "id": "uuid", "name": "ada", "email": "ada@example.com", "is_admin": false, "join_date": "..." }]

Auth: any authenticated user

{ "id": "uuid", "name": "ada", "email": "ada@example.com", "is_admin": false, "join_date": "..." }

Auth: admin

{ "name": "ada", "email": "ada@example.com", "image": "", "is_admin": false }

201 with user object. Duplicate name/email → 400.

Auth: admin. Update name, admin flag. 204 on success.

Soft-delete. Self or admin (not last admin). 204. Historical leaderboard rows show Deleted user.


Auth: required. Public problems for everyone; admins see all.

Query: ?query= (Elasticsearch when enabled).

Auth: required

Query: ?type=private includes hidden tests (admin only).

{
"id": 1,
"title": "Reverse String",
"identifier": "reverse-string",
"prompt": "...",
"difficulty": "EASY",
"public": true,
"test_cases": [{ "id": "uuid", "input": "hi", "expected_output": "ih", "hidden": false }],
"categories": [{ "id": "uuid", "name": "Strings" }]
}

Auth: admin

{
"title": "Reverse String",
"identifier": "reverse-string",
"prompt": "Reverse the input string.",
"source": "NextJudge",
"difficulty": "EASY",
"timeout": 5.0,
"accept_timeout": 5.0,
"execution_timeout": 5.0,
"memory_limit": 256,
"user_id": "admin-uuid",
"test_cases": [{ "input": "hello", "expected_output": "olleh", "hidden": false }],
"category_ids": ["uuid"],
"public": true
}

Auth: admin. Partial updates to problem metadata and tests.

DELETE /v1/problems/{problem_description_id}

Section titled “DELETE /v1/problems/{problem_description_id}”

Auth: admin. 204.

All test cases for judging. Auth: judge or admin.

Auth: required

[{ "id": "uuid", "name": "DP" }]

Enqueue for grading. Auth: required

{
"user_id": "uuid",
"problem_id": 1,
"language_id": "uuid",
"source_code": "..."
}

201:

{ "id": "uuid", "status": "PENDING", "submit_time": "...", "source_code": "..." }

Judge PATCHes status later. You poll GET until status != PENDING.

Auth: owner or admin

Full submission including test_case_results after grading.

GET /v1/submissions/{submission_id}/status

Section titled “GET /v1/submissions/{submission_id}/status”

Auth: owner or admin. Lightweight status-only poll.

Auth: judge or admin

{
"status": "ACCEPTED",
"stdout": "",
"stderr": "",
"time_elapsed": 0.042,
"failed_test_case_id": null,
"test_case_results": [{ "test_case_id": "uuid", "stdout": "...", "stderr": "", "passed": true }]
}

Auth: self or admin

GET /v1/user_problem_submissions/{user_id}/{problem_id}

Section titled “GET /v1/user_problem_submissions/{user_id}/{problem_id}”

Auth: self or admin. All attempts on one problem.


Auth: required. No test cases, arbitrary stdin.

{ "user_id": "uuid", "language_id": "uuid", "source_code": "print(input())", "stdin": "test" }

Also available unauthenticated on public/bench routes for demos (/v1/public/input_submissions, /v1/bench/input_submissions) with rate limits.

Poll until finished: true. Returns stdout, stderr, runtime.


No auth.

[{ "id": "uuid", "name": "python", "extension": "py", "version": "3.12" }]

Auth: admin. Language must exist in judge languages.toml.

Auth: admin. 204.


The API says events. The UI says contests. Same thing.

Auth: required. Contests visible to logged-in users.

Auth: required. Event + problems.

POST /v1/public/events/{event_id}/register

Section titled “POST /v1/public/events/{event_id}/register”

Auth: required. Register current user for public contest.

Auth: admin. All events.

Auth: admin

Auth: admin

{
"title": "Spring 2026",
"description": "Internal practice",
"start_time": "2026-04-01T18:00:00Z",
"end_time": "2026-04-01T21:00:00Z",
"teams": false,
"user_id": "admin-uuid"
}

Auth: admin

Auth: admin

MethodPathAuthPurpose
GET/v1/events/{id}/problemsuserProblems in contest
POST/v1/events/{id}/problemsuserAttach problem
GET/v1/events/{id}/submissionsuserFiltered submissions
GET/v1/events/{id}/attemptsuserICPC-style solve times
GET/v1/events/{id}/participantsuser/adminWho registered
POST/v1/events/{id}/participantsadminAdd participant
GET/POST/v1/events/{id}/questionsuserClarification questions

MethodPathPurpose
GET/v1/user/notifications/countUnread count
GET/v1/user/notificationsList
PUT/v1/user/notifications/mark-readMark read

All require auth.


No auth. 200 = up.


{ "error": "Human-readable message", "code": "OPTIONAL_CODE" }
HTTPMeaning
400Bad input
401Missing/invalid auth
403Wrong role
404Not found
409Conflict (e.g. user exists)
500Server blew up

Auth errors are worth memorizing: Malformed JWT token almost always means you prefixed Bearer.