Developer

API Documentation

Authentication

All API requests must include an API key in the Authorization header:

Authorization: Bearer rl_<your-key>

API keys are created and managed by admins at /admin/api-keys. Each key has one or more scopes that restrict what it can access. Keys are hashed server-side and shown only once at creation.

Scopes

ScopeAccess granted
machinesRead machine list, individual machine details, and health status
challengesRead challenge metadata (id, name, difficulty)
test-resultsRead lab health check results for all machines
usersRead user list with points and credit totals (no sensitive data)
messagingSend broadcast messages and direct messages to users

Errors

All errors return JSON with an error field:

{ "error": "Missing Authorization: Bearer <key>" }
StatusMeaning
401Missing or invalid API key
403Key does not have the required scope
404Resource not found
400Bad request — missing required fields

Machines

Requires scope: machines

GET /api/v1/machines

Returns all machines with health status.

Example response

[
  {
    "id": "http-lab",
    "name": "Bastion",
    "difficulty": "Easy",
    "difficultyRating": 1,
    "ports": [{ "service": "HTTP", "port": 80 }],
    "ipAddress": "45.56.112.197",
    "active": true,
    "isTimed": false,
    "launchAt": null,
    "health": {
      "online": true,
      "latencyMs": 12,
      "checkedAt": "2026-05-07T20:00:00.000Z"
    }
  }
]
GET /api/v1/machines/:id

Returns a single machine by its id slug.

GET /api/v1/machines/hexvault-lab

Challenges

Requires scope: challenges

GET /api/v1/challenges

Lightweight list of all challenges (id, name, difficulty, active status). Alias for machines with reduced fields.

Example response

[
  { "id": "http-lab", "name": "Bastion", "difficulty": "Easy", "difficultyRating": 1, "active": true }
]

Test Results

Requires scope: test-results

GET /api/v1/test-results

Returns the latest health check result for every lab, sorted newest first.

Example response

[
  {
    "challengeId": "http-lab",
    "online": true,
    "latencyMs": 8,
    "port": 80,
    "errorMsg": null,
    "checkedAt": "2026-05-07T21:00:00.000Z"
  }
]
GET /api/v1/test-results/:id

Returns the health record for a single machine by its id slug.

GET /api/v1/test-results/hexvault-lab

Users

Requires scope: users

GET /api/v1/users

Returns all users with public stats. No passwords, tokens, or emails are ever exposed.

Example response

[
  {
    "id": "abc123",
    "username": "xssrat",
    "isPremium": true,
    "credits": 850,
    "totalPoints": 850,
    "joinedAt": "2025-01-15T10:00:00.000Z"
  }
]

Messaging

Requires scope: messaging

POST /api/v1/messaging/broadcast

Sends a broadcast message to all users' inboxes.

Request body (JSON)

{
  "subject": "Points have been updated",
  "body": "We've retroactively applied difficulty multipliers to all submissions..."
}

Example response 201

{
  "id": "abc123",
  "subject": "Points have been updated",
  "body": "We've retroactively applied ...",
  "createdAt": "2026-05-07T20:30:00.000Z"
}
POST /api/v1/messaging/dm

Sends a direct message to a specific user.

Request body (JSON)

{
  "username": "xssrat",
  "subject": "Your points were adjusted",
  "body": "Hi! Your submission points have been updated..."
}

Example response 201

{
  "id": "xyz789",
  "toUsername": "xssrat",
  "subject": "Your points were adjusted",
  "createdAt": "2026-05-07T20:30:00.000Z"
}

curl examples

# Broadcast
curl -X POST https://ratctf.thexssrat.com/api/v1/messaging/broadcast \
  -H "Authorization: Bearer rl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Points updated","body":"Difficulty multipliers have been applied retroactively to all flag submissions. Your point totals may have changed."}'

# DM a specific user
curl -X POST https://ratctf.thexssrat.com/api/v1/messaging/dm \
  -H "Authorization: Bearer rl_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"username":"xssrat","subject":"Your points were adjusted","body":"Hi! ..."}'