{"openapi":"3.1.0","info":{"title":"Citadel Public API","version":"1.0.0","description":"Read access to your Citadel server data — status, players, leaderboards, killfeed, and bans — plus the public Trust lookup.\n\nThe Data API is authenticated with a customer API key (`ctk_…`) that you mint in your Citadel dashboard under **Account → API Keys**. A key can read only the servers owned by the account that created it. Send it as `Authorization: Bearer <key>`.\n\nThe Trust lookup is fully public (no key, per-IP rate limited).\n\nPaged endpoints accept a `limit`, but it is clamped to your key tier’s maximum page size (standard 100, partner 500) — requesting more returns at most the tier ceiling. A 429 with an `Error` body means you have exceeded your per-minute tier rate; retry after ~60s.\n\nIf an operator has disabled the Data API on their instance, `/data/*` endpoints return 503 `DATA_API_DISABLED`.\n\nNote: live player coordinates are intentionally not exposed by this API.","contact":{"name":"Citadel","url":"https://citadel-hub.com"}},"servers":[{"url":"https://api.cloud.citadel-hub.com/api/v1","description":"Production"}],"tags":[{"name":"Servers","description":"Server status and rosters"},{"name":"Stats","description":"Leaderboards and killfeed"},{"name":"Bans","description":"Per-server ban lists"},{"name":"Trust","description":"Public cross-community trust lookup"}],"security":[{"ApiKey":[]}],"paths":{"/data/servers":{"get":{"tags":["Servers"],"summary":"List your servers","description":"Every non-revoked server owned by the API key's account, with live online status.","responses":{"200":{"description":"Server list","content":{"application/json":{"schema":{"type":"object","properties":{"servers":{"type":"array","items":{"$ref":"#/components/schemas/ServerSummary"}}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/data/servers/{serverId}":{"get":{"tags":["Servers"],"summary":"Server status","parameters":[{"name":"serverId","in":"path","required":true,"description":"The Citadel server id (UUID). Find it via GET /data/servers.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Server status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServerStatus"}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/data/servers/{serverId}/players":{"get":{"tags":["Servers"],"summary":"Online players","description":"Players active in the last 90 seconds. Identities only — no coordinates.","parameters":[{"name":"serverId","in":"path","required":true,"description":"The Citadel server id (UUID). Find it via GET /data/servers.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Online roster","content":{"application/json":{"schema":{"type":"object","properties":{"players":{"type":"array","items":{"$ref":"#/components/schemas/OnlinePlayer"}},"count":{"type":"integer"}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/data/servers/{serverId}/leaderboard":{"get":{"tags":["Stats"],"summary":"Leaderboard","parameters":[{"name":"serverId","in":"path","required":true,"description":"The Citadel server id (UUID). Find it via GET /data/servers.","schema":{"type":"string","format":"uuid"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["points","kd","kills"],"default":"points"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":25}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Ranked players","content":{"application/json":{"schema":{"type":"object","properties":{"total":{"type":"integer"},"sort":{"type":"string"},"players":{"type":"array","items":{"$ref":"#/components/schemas/LeaderboardEntry"}}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/data/servers/{serverId}/kills":{"get":{"tags":["Stats"],"summary":"Killfeed","description":"PvP kills, newest first. Paginate with the `before` cursor (pass the previous `nextCursor`).","parameters":[{"name":"serverId","in":"path","required":true,"description":"The Citadel server id (UUID). Find it via GET /data/servers.","schema":{"type":"string","format":"uuid"}},{"name":"before","in":"query","description":"ISO-8601 timestamp; return kills before this instant.","schema":{"type":"string","format":"date-time"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":50}}],"responses":{"200":{"description":"Kill events","content":{"application/json":{"schema":{"type":"object","properties":{"kills":{"type":"array","items":{"$ref":"#/components/schemas/Kill"}},"nextCursor":{"type":["string","null"],"format":"date-time"}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/data/servers/{serverId}/bans":{"get":{"tags":["Bans"],"summary":"Server ban list","parameters":[{"name":"serverId","in":"path","required":true,"description":"The Citadel server id (UUID). Find it via GET /data/servers.","schema":{"type":"string","format":"uuid"}},{"name":"active","in":"query","description":"Filter to currently-active (true) or expired (false) bans.","schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":100}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Bans","content":{"application/json":{"schema":{"type":"object","properties":{"bans":{"type":"array","items":{"$ref":"#/components/schemas/Ban"}}}}}}},"401":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/trust/{identifier}":{"get":{"tags":["Trust"],"summary":"Public trust lookup","description":"Look up a player's explainable trust score across the Citadel network. Public — no API key required. Accepts a SteamID64, Steam2 id, Citadel ID (UUID), or Discord id. Per-IP rate limited.","security":[],"parameters":[{"name":"identifier","in":"path","required":true,"schema":{"type":"string"},"description":"SteamID64 / Steam2 / Citadel ID / Discord id"}],"responses":{"200":{"description":"Trust score","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustScore"}}}},"404":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","description":"Customer API key (`ctk_…`) from your Citadel dashboard. Also accepted as the `X-Citadel-Api-Key` header."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable error code.","example":"NOT_FOUND"},"message":{"type":"string"},"statusCode":{"type":"integer"},"requestId":{"type":"string","description":"Quote this to support when reporting a failure."}},"required":["error","message","statusCode"]},"ServerSummary":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"map":{"type":["string","null"]},"gameVersion":{"type":["string","null"]},"online":{"type":"boolean"},"lastSeenAt":{"type":["string","null"],"format":"date-time"}}},"ServerStatus":{"allOf":[{"$ref":"#/components/schemas/ServerSummary"},{"type":"object","properties":{"players":{"type":["integer","null"],"description":"Current player count (latest metrics sample)."},"fps":{"type":["number","null"]},"uptimeSeconds":{"type":["integer","null"]},"metricsAt":{"type":["string","null"],"format":"date-time"}}}]},"OnlinePlayer":{"type":"object","properties":{"steamId":{"type":"string"},"name":{"type":"string"}}},"LeaderboardEntry":{"type":"object","properties":{"rank":{"type":"integer"},"steamId":{"type":"string"},"name":{"type":"string"},"kills":{"type":"integer"},"deaths":{"type":"integer"},"headshots":{"type":"integer"},"kd":{"type":"number"},"points":{"type":"integer"},"meanKillDistance":{"type":["number","null"],"description":"Average kill distance in metres."},"playMinutes":{"type":"integer"}}},"Kill":{"type":"object","properties":{"ts":{"type":"string","format":"date-time"},"killerSteamId":{"type":"string"},"killerName":{"type":"string"},"victimSteamId":{"type":"string"},"victimName":{"type":"string"},"weapon":{"type":"string"},"distance":{"type":"number","description":"Metres."},"isHeadshot":{"type":"boolean"},"hitZone":{"type":["string","null"]}}},"Ban":{"type":"object","properties":{"steamId":{"type":"string"},"playerName":{"type":["string","null"]},"reason":{"type":["string","null"]},"source":{"type":["string","null"],"description":"manual | import | cloud"},"permanent":{"type":"boolean"},"bannedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"}}},"TrustScore":{"type":"object","properties":{"steamId":{"type":"string"},"citadelId":{"type":["string","null"]},"score":{"type":"integer","minimum":0,"maximum":100},"confidence":{"type":"number","minimum":0,"maximum":1},"band":{"type":"string","enum":["trusted","neutral","watch","high_risk"]},"signals":{"type":"object","description":"The raw signals the score is computed from (transparent by design).","properties":{"bansActive":{"type":"integer"},"submissionsPending":{"type":"integer"},"overturns":{"type":"integer"},"playMinutes":{"type":"integer"},"serversSeen":{"type":"integer"},"kills":{"type":"integer"},"headshotRatio":{"type":["number","null"]},"accuracy":{"type":["number","null"]},"meanKillDistance":{"type":["number","null"]}}},"reasons":{"type":"array","items":{"type":"string"}},"computedAt":{"type":"string","format":"date-time"}}}}}}