{
  "openapi": "3.1.0",
  "info": {
    "title": "Ginkgo Playtest Analytics API",
    "version": "1.0.0",
    "description": "Build-scoped playtest reports, raw events, level maps, and build-time movement profiles. Read endpoints are public and not rate limited."
  },
  "servers": [{ "url": "https://ginkgo.maxceem.bid" }],
  "paths": {
    "/api/v1/builds": {
      "get": { "operationId": "listBuilds", "summary": "List published builds with verified telemetry", "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" } } }
    },
    "/api/v1/builds/{build}": {
      "get": { "operationId": "getBuild", "summary": "Get one build and its level manifest", "parameters": [{ "$ref": "#/components/parameters/Build" }], "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/builds/{build}/movement-profile": {
      "get": { "operationId": "getMovementProfile", "summary": "Get build-time character movement capabilities", "parameters": [{ "$ref": "#/components/parameters/Build" }], "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/builds/{build}/levels": {
      "get": { "operationId": "listLevels", "summary": "List levels and immutable map identifiers in a build", "parameters": [{ "$ref": "#/components/parameters/Build" }], "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/builds/{build}/events": {
      "get": { "operationId": "listBuildEvents", "summary": "Page through raw normalized events for a build", "parameters": [{ "$ref": "#/components/parameters/Build" }, { "$ref": "#/components/parameters/LevelFilter" }, { "$ref": "#/components/parameters/Session" }, { "$ref": "#/components/parameters/EventFilter" }, { "$ref": "#/components/parameters/Tester" }, { "$ref": "#/components/parameters/Limit" }, { "$ref": "#/components/parameters/Cursor" }], "responses": { "200": { "$ref": "#/components/responses/EventPage" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/builds/{build}/levels/{level}/report": {
      "get": { "operationId": "getLevelReport", "summary": "Get the decision-ready analytics report used by the dashboard", "parameters": [{ "$ref": "#/components/parameters/Build" }, { "$ref": "#/components/parameters/Level" }, { "$ref": "#/components/parameters/Session" }], "responses": { "200": { "description": "Level report", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LevelReport" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/builds/{build}/levels/{level}/events": {
      "get": { "operationId": "listLevelEvents", "summary": "Page through raw normalized events for one level", "parameters": [{ "$ref": "#/components/parameters/Build" }, { "$ref": "#/components/parameters/Level" }, { "$ref": "#/components/parameters/Session" }, { "$ref": "#/components/parameters/EventFilter" }, { "$ref": "#/components/parameters/Tester" }, { "$ref": "#/components/parameters/Limit" }, { "$ref": "#/components/parameters/Cursor" }], "responses": { "200": { "$ref": "#/components/responses/EventPage" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/compare": {
      "get": { "operationId": "compareBuilds", "summary": "Compare the same level between two builds", "parameters": [{ "name": "from", "in": "query", "required": true, "schema": { "type": "string" } }, { "name": "to", "in": "query", "required": true, "schema": { "type": "string" } }, { "name": "level", "in": "query", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/v1/version": {
      "get": { "operationId": "getSiteVersion", "summary": "Get the deployed commit and when it was deployed", "description": "commit is the short Git hash (null outside a deploy), dirty is true when the Atlas had uncommitted changes, deployedAt is when this Worker version was uploaded.", "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" } } }
    },
    "/api/v1/refresh/status": {
      "get": { "operationId": "getRefreshStatus", "summary": "Get the Amplitude import status", "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" } } }
    },
    "/api/v1/refresh": {
      "post": { "operationId": "startRefresh", "summary": "Start or join an Amplitude import", "description": "Globally limited to one accepted manual start per 300 seconds. A request made while a job is running joins that job.", "responses": { "202": { "$ref": "#/components/responses/JsonSuccess" }, "429": { "description": "Global cooldown is active. Honor Retry-After.", "headers": { "Retry-After": { "schema": { "type": "integer" }, "description": "Seconds until another manual refresh may be accepted." } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefreshStatus" } } } } } }
    },
    "/api/refresh": {
      "post": { "operationId": "startRefreshCompatibility", "summary": "Start or join an Amplitude import (stable compatibility path)", "description": "Uses the same global job and 300-second cooldown as /api/v1/refresh.", "responses": { "202": { "$ref": "#/components/responses/JsonSuccess" }, "429": { "description": "Global cooldown is active. Honor Retry-After.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefreshStatus" } } } } } }
    },
    "/api/levels/{mapAssetId}/metadata.json": {
      "get": { "operationId": "getLevelMapMetadata", "summary": "Get structured level bounds, checkpoints, and hazards", "parameters": [{ "$ref": "#/components/parameters/MapAssetId" }], "responses": { "200": { "$ref": "#/components/responses/JsonSuccess" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/levels/{mapAssetId}/map.png": {
      "get": { "operationId": "getLevelMapImage", "summary": "Get the rendered level map", "parameters": [{ "$ref": "#/components/parameters/MapAssetId" }], "responses": { "200": { "description": "PNG map", "content": { "image/png": { "schema": { "type": "string", "contentEncoding": "binary" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } }
    }
  },
  "components": {
    "parameters": {
      "Build": { "name": "build", "in": "path", "required": true, "description": "Numeric build number", "schema": { "type": "string", "pattern": "^[0-9]+$" } },
      "Level": { "name": "level", "in": "path", "required": true, "schema": { "type": "string" } },
      "LevelFilter": { "name": "level", "in": "query", "schema": { "type": "string" } },
      "Session": { "name": "session", "in": "query", "description": "Exact run_id (or fallback session identity)", "schema": { "type": "string" } },
      "EventFilter": { "name": "event", "in": "query", "schema": { "type": "string" } },
      "Tester": { "name": "tester", "in": "query", "description": "Pseudonymous user_id", "schema": { "type": "string" } },
      "Limit": { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000, "default": 250 } },
      "Cursor": { "name": "cursor", "in": "query", "description": "Use page.nextCursor from the previous response", "schema": { "type": "string" } },
      "MapAssetId": { "name": "mapAssetId", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^[a-f0-9]{32,64}$" } }
    },
    "responses": {
      "JsonSuccess": { "description": "JSON response", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": true } } } },
      "EventPage": { "description": "Paginated events", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventPage" } } } },
      "BadRequest": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string" } } },
      "EventPage": { "type": "object", "required": ["apiVersion", "items", "page"], "properties": { "apiVersion": { "const": "1" }, "items": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "page": { "type": "object", "required": ["limit", "total", "nextCursor"], "properties": { "limit": { "type": "integer" }, "total": { "type": "integer" }, "nextCursor": { "type": ["string", "null"] } } } } },
      "LevelReport": { "type": "object", "required": ["apiVersion", "scope", "snapshot", "build", "level", "metrics", "deathCauses", "dangerousAreas", "methodology"], "properties": { "apiVersion": { "const": "1" }, "scope": { "type": "object", "additionalProperties": true }, "snapshot": { "type": "object", "additionalProperties": true }, "build": { "type": "object", "additionalProperties": true }, "level": { "type": "object", "additionalProperties": true }, "movementProfile": { "type": ["object", "null"], "additionalProperties": true }, "metrics": { "type": "object", "additionalProperties": { "type": ["number", "null"] } }, "deathCauses": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "dangerousAreas": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "methodology": { "type": "object", "additionalProperties": true } } },
      "RefreshStatus": { "type": "object", "required": ["apiVersion", "running", "rateLimit"], "properties": { "apiVersion": { "const": "1" }, "running": { "type": "boolean" }, "jobId": { "type": "string" }, "message": { "type": "string" }, "rateLimit": { "type": "object", "properties": { "cooldownSeconds": { "type": "integer", "const": 300 }, "retryAfterSeconds": { "type": "integer" } } } } }
    }
  }
}
