{
    "openapi": "3.0.3",
    "info": {
        "title": "VDS Pro API",
        "version": "1.0.0",
        "description": "Public REST API for the VDS Pro Unity SDK and integration partners. All endpoints require a Bearer token issued by Cognito (or a server-signed JWT for Steam users) in the `Authorization` header.",
        "contact": {
            "name": "VDS Pro Support",
            "email": "support@vdspro.net",
            "url": "https://www.vdspro.net/support"
        },
        "license": {
            "name": "Proprietary"
        }
    },
    "servers": [
        { "url": "https://www.vdspro.net/api/v1", "description": "Production" }
    ],
    "security": [
        { "BearerAuth": [] }
    ],
    "tags": [
        { "name": "health", "description": "Lightweight liveness probe for SDK clients" },
        { "name": "session", "description": "Concurrent-session lifecycle for the simulator" },
        { "name": "saves", "description": "Cloud-saved game state" },
        { "name": "capabilities", "description": "Per-user feature flags resolved from subscription / org" },
        { "name": "progress", "description": "Module progress and aggregate stats" },
        { "name": "assessments", "description": "Instructor-assigned assessments" },
        { "name": "certificates", "description": "Pass certificates issued on assessment completion" },
        { "name": "link", "description": "Account linking (Steam, etc.)" }
    ],
    "components": {
        "securitySchemes": {
            "BearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "error": { "type": "string", "description": "Stable machine-readable error code" }
                },
                "required": ["error"]
            },
            "ProgressEvent": {
                "type": "object",
                "properties": {
                    "moduleId": { "type": "string" },
                    "score": { "type": "integer" },
                    "maxScore": { "type": "integer" },
                    "passed": { "type": "boolean" },
                    "completionPercent": { "type": "integer", "minimum": 0, "maximum": 100 },
                    "duration": { "type": "integer", "description": "Seconds" },
                    "metadata": { "type": "object", "additionalProperties": true },
                    "createdAt": { "type": "string", "format": "date-time" }
                },
                "required": ["moduleId", "score", "createdAt"]
            },
            "ProgressEvidence": {
                "type": "object",
                "description": "Optional Unity lesson evidence stored under metadata.unityEvidence for reports and exports.",
                "properties": {
                    "eventType": {
                        "type": "string",
                        "enum": ["module_attempt", "lesson_started", "lesson_completed", "scenario_checkpoint", "hazard_event", "instructor_note", "custom"]
                    },
                    "sessionId": { "type": "string" },
                    "lessonId": { "type": "string" },
                    "lessonName": { "type": "string" },
                    "scenarioId": { "type": "string" },
                    "scenarioName": { "type": "string" },
                    "occurredAt": { "type": "string", "format": "date-time" },
                    "focusAreas": { "type": "array", "items": { "type": "string" } },
                    "conditions": { "type": "array", "items": { "type": "string" } },
                    "metrics": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "key": { "type": "string" },
                                "value": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
                                "unit": { "type": "string" }
                            }
                        }
                    },
                    "incidents": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "type": { "type": "string" },
                                "severity": { "type": "string" },
                                "count": { "type": "integer" },
                                "notes": { "type": "string" }
                            }
                        }
                    },
                    "scoreBreakdown": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {
                                "skill": { "type": "string" },
                                "score": { "type": "integer" },
                                "maxScore": { "type": "integer" },
                                "passed": { "type": "boolean" },
                                "notes": { "type": "string" }
                            }
                        }
                    },
                    "instructorNotes": { "type": "string" },
                    "learnerNotes": { "type": "string" },
                    "nextSteps": { "type": "array", "items": { "type": "string" } }
                }
            },
            "Capability": {
                "type": "object",
                "properties": {
                    "subscriptionStatus": { "type": "string", "enum": ["trial", "active", "expired", "cancelled", "deleted"] },
                    "trialEndsAt": { "type": "string", "format": "date-time", "nullable": true },
                    "products": { "type": "array", "items": { "type": "string" } },
                    "purchases": { "type": "array", "items": { "type": "string" } }
                }
            },
            "Assessment": {
                "type": "object",
                "properties": {
                    "assessmentId": { "type": "string" },
                    "templateId": { "type": "string" },
                    "templateName": { "type": "string" },
                    "userId": { "type": "string" },
                    "orgId": { "type": "string", "nullable": true },
                    "status": { "type": "string", "enum": ["assigned", "in-progress", "submitted", "reviewed", "passed", "failed"] },
                    "attemptNumber": { "type": "integer" },
                    "percentScore": { "type": "integer" },
                    "passed": { "type": "boolean" },
                    "createdAt": { "type": "string", "format": "date-time" }
                }
            }
        }
    },
    "paths": {
        "/health": {
            "get": {
                "tags": ["health"],
                "summary": "Liveness probe",
                "description": "Returns 200 with a small JSON body. Public; does not require a Bearer token.",
                "security": [],
                "responses": {
                    "200": {
                        "description": "API is up",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object",
                                    "properties": {
                                        "status": { "type": "string", "example": "ok" },
                                        "api": { "type": "string", "example": "v1" },
                                        "timestamp": { "type": "string", "format": "date-time" }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/session/start": {
            "post": {
                "tags": ["session"],
                "summary": "Acquire a concurrent-session slot",
                "description": "Reserves a seat in the org's concurrency limit (or the user's personal subscription) for the duration of the simulator run. Returns 429 if no seats are available.",
                "responses": {
                    "200": { "description": "Session acquired" },
                    "401": { "description": "Missing or invalid bearer token" },
                    "429": { "description": "Concurrency limit reached" }
                }
            }
        },
        "/session/heartbeat": {
            "post": {
                "tags": ["session"],
                "summary": "Keep the session alive",
                "description": "Must be called every ~60 seconds. Sessions without a recent heartbeat are reclaimed to free seats.",
                "responses": {
                    "200": { "description": "Heartbeat acknowledged" },
                    "401": { "description": "Missing or invalid bearer token" }
                }
            }
        },
        "/session/end": {
            "post": {
                "tags": ["session"],
                "summary": "Release the session slot",
                "responses": {
                    "200": { "description": "Session released" }
                }
            }
        },
        "/saves": {
            "post": {
                "tags": ["saves"],
                "summary": "Upload a cloud save",
                "responses": {
                    "200": { "description": "Save accepted" }
                }
            }
        },
        "/saves/{productId}": {
            "get": {
                "tags": ["saves"],
                "summary": "Fetch the latest save for a product",
                "parameters": [
                    { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Save payload" },
                    "404": { "description": "No save exists" }
                }
            }
        },
        "/saves/{productId}/list": {
            "get": {
                "tags": ["saves"],
                "summary": "List all saved slots for a product",
                "parameters": [
                    { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Array of save metadata" }
                }
            }
        },
        "/saves/{productId}/{slot}": {
            "delete": {
                "tags": ["saves"],
                "summary": "Delete a save slot",
                "parameters": [
                    { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } },
                    { "name": "slot", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Save deleted" }
                }
            }
        },
        "/capabilities": {
            "get": {
                "tags": ["capabilities"],
                "summary": "Resolve the user's feature capabilities",
                "description": "Returns the user's product subscriptions, trial status, and purchased content packs. The simulator client should call this on launch to gate paid features.",
                "responses": {
                    "200": {
                        "description": "Capabilities resolved",
                        "content": {
                            "application/json": {
                                "schema": { "$ref": "#/components/schemas/Capability" }
                            }
                        }
                    }
                }
            }
        },
        "/progress": {
            "post": {
                "tags": ["progress"],
                "summary": "Record a module attempt",
                "description": "Records progress from Unity. Include `evidence` when the simulator has lesson-level data for reports, CSV exports, instructor review, or compliance records.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": ["productId", "moduleId"],
                                "properties": {
                                    "productId": { "type": "string", "example": "vdspro" },
                                    "moduleId": { "type": "string", "example": "roundabouts" },
                                    "score": { "type": "integer", "example": 62 },
                                    "maxScore": { "type": "integer", "example": 100 },
                                    "passed": { "type": "boolean", "example": false },
                                    "completionPercent": { "type": "integer", "minimum": 0, "maximum": 100, "example": 80 },
                                    "duration": { "type": "integer", "description": "Seconds", "example": 1200 },
                                    "metadata": { "type": "object", "additionalProperties": true },
                                    "evidence": { "$ref": "#/components/schemas/ProgressEvidence" }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": { "description": "Recorded" }
                }
            },
            "get": {
                "tags": ["progress"],
                "summary": "Aggregate progress across all products",
                "responses": {
                    "200": { "description": "Per-product summaries" }
                }
            }
        },
        "/progress/{productId}": {
            "get": {
                "tags": ["progress"],
                "summary": "Per-product progress events",
                "parameters": [
                    { "name": "productId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": {
                        "description": "List of progress events",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": { "$ref": "#/components/schemas/ProgressEvent" }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/assessments": {
            "get": {
                "tags": ["assessments"],
                "summary": "List assessments assigned to the user",
                "responses": {
                    "200": {
                        "description": "Array of assessments",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "array",
                                    "items": { "$ref": "#/components/schemas/Assessment" }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/assessments/{assessmentId}": {
            "get": {
                "tags": ["assessments"],
                "summary": "Get one assessment",
                "parameters": [
                    { "name": "assessmentId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Assessment detail" },
                    "404": { "description": "Assessment not found or not assigned to caller" }
                }
            }
        },
        "/assessments/{assessmentId}/start": {
            "post": {
                "tags": ["assessments"],
                "summary": "Mark assessment as in-progress",
                "parameters": [
                    { "name": "assessmentId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Started" }
                }
            }
        },
        "/assessments/{assessmentId}/submit": {
            "post": {
                "tags": ["assessments"],
                "summary": "Submit assessment scores",
                "parameters": [
                    { "name": "assessmentId", "in": "path", "required": true, "schema": { "type": "string" } }
                ],
                "responses": {
                    "200": { "description": "Submitted; awaiting instructor review" }
                }
            }
        },
        "/certificates": {
            "get": {
                "tags": ["certificates"],
                "summary": "List the user's pass certificates",
                "responses": {
                    "200": { "description": "Array of certificate refs" }
                }
            }
        },
        "/link-steam": {
            "post": {
                "tags": ["link"],
                "summary": "Link a Steam ID to the current user",
                "responses": {
                    "200": { "description": "Linked" },
                    "409": { "description": "Steam ID already linked to another account" }
                }
            }
        }
    }
}
