{
  "openapi": "3.1.0",
  "info": {
    "title": "V-Sum HTTP API",
    "version": "1.36.0",
    "description": "OpenAPI for v-sum.com site and operator HTTP routes (this file). Read-only structured data for agents is also available at https://api.v-sum.com with a separate OpenAPI document at https://api.v-sum.com/spec.json — see /developers and /.well-known/api-catalog. Operator routes require a Google-authenticated staff session. MCP (JSON-RPC) is served at POST /mcp — see /developers and /.well-known/mcp/server-card.json. Unknown /api/* paths return 404 JSON (not 401). Rate limits: selective routes return 429 with Retry-After (see /developers). Status: GET /api/health and human page /status.",
    "contact": {
      "name": "V-Sum",
      "email": "hello@v-sum.com",
      "url": "https://v-sum.com/contact"
    },
    "x-mcp": {
      "endpoint": "https://v-sum.com/mcp",
      "protocolVersions": ["2025-06-18"],
      "serverCard": "https://v-sum.com/.well-known/mcp/server-card.json",
      "note": "POST JSON-RPC 2.0. Send Accept: application/json, text/event-stream. Lifecycle notifications use HTTP 202."
    },
    "x-sdks": {
      "npm": [
        { "name": "v-sum", "url": "https://www.npmjs.com/package/v-sum", "primary": true },
        { "name": "vsum-api-client", "url": "https://www.npmjs.com/package/vsum-api-client" },
        { "name": "vsum-cli", "url": "https://www.npmjs.com/package/vsum-cli" }
      ],
      "pypi": [
        { "name": "vsum-api-client", "url": "https://pypi.org/project/vsum-api-client/", "primary": true }
      ],
      "go": [
        { "name": "github.com/superduperdot/vsum-sdk-go", "url": "https://pkg.go.dev/github.com/superduperdot/vsum-sdk-go", "primary": true }
      ]
    },
    "x-ai-plugin": "https://v-sum.com/.well-known/ai-plugin.json"
  },
  "servers": [{ "url": "https://v-sum.com" }],
  "tags": [
    { "name": "Public", "description": "Unauthenticated public endpoints" },
    { "name": "Portal", "description": "Attendee portal (magic link session)" },
    { "name": "Operator", "description": "Staff console — session required" }
  ],
  "components": {
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["status", "service"],
        "properties": {
          "status": { "type": "string", "enum": ["ok"] },
          "service": { "type": "string", "enum": ["v-sum"] }
        }
      },
      "ApplyRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["firstName", "lastName", "email", "companyName"],
        "properties": {
          "firstName": { "type": "string", "maxLength": 100, "minLength": 1 },
          "lastName": { "type": "string", "maxLength": 100, "minLength": 1 },
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "title": { "type": "string", "maxLength": 200 },
          "companyName": { "type": "string", "maxLength": 200, "minLength": 1 },
          "companyWebsite": { "type": "string", "maxLength": 500 },
          "whatTheyAreBuilding": { "type": "string", "maxLength": 5000 },
          "whyNow": { "type": "string", "maxLength": 5000 },
          "linkedin": { "type": "string", "maxLength": 500 },
          "eventId": { "type": "string", "maxLength": 100 }
        }
      },
      "ApplySuccess": {
        "type": "object",
        "additionalProperties": false,
        "required": ["success", "applicationId"],
        "properties": {
          "success": { "type": "boolean", "const": true },
          "applicationId": { "type": "string" }
        }
      },
      "NominateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": ["nomineeName", "reason"],
        "properties": {
          "nominatorName": { "type": "string", "maxLength": 200 },
          "nominatorEmail": { "type": "string", "format": "email", "maxLength": 254 },
          "nomineeName": { "type": "string", "maxLength": 200, "minLength": 1 },
          "nomineeEmail": { "type": "string", "format": "email", "maxLength": 254 },
          "nomineeCompanyName": { "type": "string", "maxLength": 200 },
          "reason": { "type": "string", "maxLength": 5000, "minLength": 1 }
        }
      },
      "ErrorBody": {
        "type": "object",
        "additionalProperties": true,
        "required": ["error"],
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" },
          "details": {
            "type": "array",
            "items": { "type": "string" }
          },
          "documentation": { "type": "string", "format": "uri" }
        }
      },
      "NotFoundError": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error", "message"],
        "properties": {
          "error": { "type": "string", "const": "not_found" },
          "message": { "type": "string" },
          "documentation": { "type": "string", "format": "uri" }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation failed or malformed JSON",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": {
              "validation": {
                "value": { "error": "Invalid input", "details": ["email must be valid"] }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentication required (portal or operator session)",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": {
              "portal": { "value": { "error": "Sign in required" } },
              "operator": { "value": { "error": "Unauthorized" } }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not permitted",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": { "sample": { "value": { "error": "Admin access required" } } }
          }
        }
      },
      "NotFound": {
        "description": "No matching API route",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/NotFoundError" },
            "examples": {
              "sample": {
                "value": {
                  "error": "not_found",
                  "message": "No API route exists for this path.",
                  "documentation": "https://v-sum.com/developers"
                }
              }
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded — retry after Retry-After seconds",
        "headers": {
          "Retry-After": {
            "schema": { "type": "integer" },
            "description": "Seconds until the client may retry"
          }
        },
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": { "sample": { "value": { "error": "Too many requests" } } }
          }
        }
      },
      "ServerError": {
        "description": "Unexpected server error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": { "sample": { "value": { "error": "Server error" } } }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "Dependency unavailable (e.g. database)",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorBody" },
            "examples": { "sample": { "value": { "error": "Service unavailable" } } }
          }
        }
      }
    }
  },
  "paths": {
    "/api/health": {
      "get": {
        "tags": ["Public"],
        "summary": "Service health",
        "operationId": "getApiHealth",
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/HealthResponse" }
              }
            }
          },
          "503": { "$ref": "#/components/responses/ServiceUnavailable" },
          "default": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/apply": {
      "post": {
        "tags": ["Public"],
        "summary": "Submit presenter application",
        "operationId": "postApiApply",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ApplyRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Application accepted",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApplySuccess" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "default": { "$ref": "#/components/responses/ServerError" }
        }
      }
    },
    "/api/nominate": {
      "post": {
        "tags": ["Portal"],
        "summary": "Submit nomination (portal session required)",
        "operationId": "postApiNominate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/NominateRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nomination recorded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "properties": {
                    "success": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooManyRequests" },
          "500": { "$ref": "#/components/responses/ServerError" },
          "default": { "$ref": "#/components/responses/ServerError" }
        }
      }
    }
  }
}
