{
  "openapi": "3.0.3",
  "info": {
    "title": "onepizza.io API",
    "version": "1.3.0",
    "description": "RESTful + WebSocket API for creating and managing video meetings. Built for developers, agents, and teams.\n\nBase URL: https://onepizza.io\n\nAuthentication:\n- API key via `x-api-key` header (issued from Dashboard → API Keys)\n- Admin token via `x-admin-token` header (returned when a meeting is created; identifies the meeting host)\n- Session cookie (`connect.sid`) for browser/dashboard endpoints",
    "contact": { "url": "https://onepizza.io/docs" },
    "license": { "name": "Proprietary" }
  },
  "servers": [
    { "url": "https://onepizza.io", "description": "Production" },
    { "url": "http://localhost:3000", "description": "Local development" }
  ],
  "security": [{ "apiKey": [] }],
  "tags": [
    { "name": "Auth", "description": "Account registration, login, password management" },
    { "name": "User", "description": "Profile and user-scoped helpers" },
    { "name": "API Keys", "description": "Manage API keys" },
    { "name": "Meetings", "description": "Create, list, manage meetings" },
    { "name": "Guest Meetings", "description": "No-auth meeting creation (rate-limited)" },
    { "name": "Participants", "description": "Mute, unmute, kick participants; lock meetings" },
    { "name": "Polls", "description": "Live polls inside a meeting" },
    { "name": "Q&A", "description": "Audience questions and host answers" },
    { "name": "Notes", "description": "Shared collaborative meeting notes" },
    { "name": "Files", "description": "File sharing during a meeting" },
    { "name": "Attendance", "description": "Per-meeting attendance reporting" },
    { "name": "Templates", "description": "Reusable meeting templates" },
    { "name": "Recurring", "description": "Recurring meeting schedules" },
    { "name": "Transcripts", "description": "Chat transcripts" },
    { "name": "Recordings", "description": "Upload, list, download, delete recordings" },
    { "name": "Webhooks", "description": "Webhook subscriptions and event delivery" },
    { "name": "Billing", "description": "Stripe checkout, USDC deposits, credit history" },
    { "name": "Company", "description": "Company accounts, team members" },
    { "name": "Config", "description": "Public client configuration" },
    { "name": "SFU", "description": "LiveKit SFU configuration and tokens" },
    { "name": "Health", "description": "Liveness / readiness probes" },
    { "name": "Admin: Users", "description": "Admin panel — user management" },
    { "name": "Admin: Meetings", "description": "Admin panel — live and historical meetings" },
    { "name": "Admin: Settings", "description": "Admin panel — feature flags and pricing" },
    { "name": "Admin: Keys", "description": "Admin panel — API key management" },
    { "name": "Admin: Companies", "description": "Admin panel — company management" },
    { "name": "Admin: Billing", "description": "Admin panel — billing transactions and credits" },
    { "name": "Admin: Crypto", "description": "Admin panel — USDC monitor configuration" },
    { "name": "Admin: Analytics", "description": "Admin panel — usage analytics, retention, AI usage" }
  ],
  "paths": {

    "/api/auth/register": {
      "post": {
        "tags": ["Auth"], "summary": "Register a new account", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterRequest" } } } },
        "responses": {
          "200": { "description": "Account created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "description": "Registration disabled" },
          "409": { "description": "Email already registered" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "tags": ["Auth"], "summary": "Sign in (sets session cookie)", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" } } } } } },
        "responses": {
          "200": { "description": "Login successful", "content": { "application/json": { "schema": { "type": "object", "properties": { "message": { "type": "string" }, "isAdmin": { "type": "boolean" } } } } } },
          "401": { "description": "Invalid credentials" },
          "403": { "description": "Account disabled" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "tags": ["Auth"], "summary": "Sign out (destroys session)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Logged out" } }
      }
    },
    "/api/auth/logout-all": {
      "post": {
        "tags": ["Auth"], "summary": "Revoke other browser sessions", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Other sessions revoked" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/auth/me": {
      "get": {
        "tags": ["Auth"], "summary": "Get current user", "security": [{ "session": [] }],
        "responses": {
          "200": { "description": "User profile", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UserProfile" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/api/auth/security-events": {
      "get": {
        "tags": ["Auth"], "summary": "List recent login/security events", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Recent login events" }, "401": { "$ref": "#/components/responses/Unauthorized" } }
      }
    },
    "/api/auth/forgot-password": {
      "post": {
        "tags": ["Auth"], "summary": "Request a password-reset email", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } } } } },
        "responses": { "200": { "description": "Always returns 200 to prevent email enumeration" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/auth/reset-password": {
      "get": {
        "tags": ["Auth"], "summary": "Validate a reset token", "security": [],
        "parameters": [{ "name": "token", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Token is valid" }, "400": { "description": "Invalid or expired token" } }
      },
      "post": {
        "tags": ["Auth"], "summary": "Set a new password using a reset token", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["token", "newPassword"], "properties": { "token": { "type": "string" }, "newPassword": { "type": "string", "minLength": 8, "maxLength": 128 } } } } } },
        "responses": { "200": { "description": "Password updated" }, "400": { "description": "Invalid token / password" } }
      }
    },
    "/api/auth/change-password": {
      "post": {
        "tags": ["Auth"], "summary": "Change password while signed in", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["currentPassword", "newPassword"], "properties": { "currentPassword": { "type": "string" }, "newPassword": { "type": "string", "minLength": 8, "maxLength": 128 } } } } } },
        "responses": { "200": { "description": "Password changed" }, "401": { "description": "Current password incorrect" } }
      }
    },

    "/api/user/keys": {
      "get": {
        "tags": ["API Keys"], "summary": "List your API keys", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Array of keys", "content": { "application/json": { "schema": { "type": "object", "properties": { "keys": { "type": "array", "items": { "$ref": "#/components/schemas/ApiKey" } } } } } } } }
      },
      "post": {
        "tags": ["API Keys"], "summary": "Create a new API key", "security": [{ "session": [] }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "label": { "type": "string", "maxLength": 100 } } } } } },
        "responses": { "201": { "description": "Key created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiKey" } } } } }
      }
    },
    "/api/user/keys/{id}": {
      "delete": {
        "tags": ["API Keys"], "summary": "Revoke an API key", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Key revoked" } }
      }
    },
    "/api/user/support-key": {
      "post": {
        "tags": ["User"], "summary": "Generate a single-use support key (24h expiry)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Support key", "content": { "application/json": { "schema": { "type": "object", "properties": { "key": { "type": "string" }, "expiresAt": { "type": "string", "format": "date-time" }, "message": { "type": "string" } } } } } } }
      }
    },

    "/api/config": {
      "get": {
        "tags": ["Config"], "summary": "Public client configuration (feature flags)", "security": [],
        "responses": { "200": { "description": "Feature flags", "content": { "application/json": { "schema": { "type": "object", "properties": { "recordingEnabled": { "type": "boolean" }, "screenShareEnabled": { "type": "boolean" }, "blurEnabled": { "type": "boolean" }, "registrationEnabled": { "type": "boolean" }, "captionsEnabled": { "type": "boolean" }, "guestMeetingsEnabled": { "type": "boolean" }, "pollsEnabled": { "type": "boolean" }, "qaEnabled": { "type": "boolean" }, "fileSharingEnabled": { "type": "boolean" }, "meetingNotesEnabled": { "type": "boolean" }, "breakoutRoomsEnabled": { "type": "boolean" }, "liveStreamingEnabled": { "type": "boolean" }, "e2eeEnabled": { "type": "boolean" }, "spatialAudioEnabled": { "type": "boolean" }, "networkStatsEnabled": { "type": "boolean" } } } } } } }
      }
    },
    "/api/config/ice-servers": {
      "get": {
        "tags": ["Config"], "summary": "ICE/TURN server configuration for WebRTC", "security": [],
        "responses": { "200": { "description": "ICE server list", "content": { "application/json": { "schema": { "type": "object", "properties": { "iceServers": { "type": "array", "items": { "type": "object", "properties": { "urls": { "oneOf": [ { "type": "string" }, { "type": "array", "items": { "type": "string" } } ] }, "username": { "type": "string" }, "credential": { "type": "string" } } } } } } } } } }
      }
    },

    "/api/sfu/config": {
      "get": {
        "tags": ["SFU"], "summary": "Get LiveKit SFU configuration", "security": [],
        "responses": { "200": { "description": "SFU info", "content": { "application/json": { "schema": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "url": { "type": "string", "nullable": true }, "threshold": { "type": "integer", "description": "Advisory participant count for SFU clients" }, "meshParticipantCap": { "type": "integer", "description": "Participant cap when the bundled mesh client is authoritative" }, "roomParticipantCap": { "type": "integer", "description": "Effective participant cap for this deployment" }, "sfuMaxParticipants": { "type": "integer", "description": "Participant cap when the bundled SFU client is enabled" }, "clientIntegrated": { "type": "boolean", "description": "Whether the bundled meeting UI can switch to SFU media" }, "autoJoin": { "type": "boolean", "description": "Whether the bundled client will auto-join SFU media" } } } } } } }
      }
    },
    "/api/sfu/token": {
      "post": {
        "tags": ["SFU"], "summary": "Mint a LiveKit access token for a participant", "security": [],
        "description": "Requires the per-participant `sfuAuthToken` issued at meeting join time. Returns 503 if SFU is not configured.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["meetingId", "participantId", "sfuAuthToken"], "properties": { "meetingId": { "type": "string" }, "participantId": { "type": "string" }, "sfuAuthToken": { "type": "string" }, "name": { "type": "string", "description": "Display name (server overrides with participant.name if known)" } } } } } },
        "responses": {
          "200": { "description": "Access token", "content": { "application/json": { "schema": { "type": "object", "properties": { "token": { "type": "string" }, "url": { "type": "string" } } } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "403": { "description": "Not a participant or invalid sfuAuthToken" },
          "404": { "description": "Meeting not found" },
          "503": { "description": "SFU not configured" }
        }
      }
    },

    "/api/meetings/{meetingId}/quality": {
      "post": {
        "tags": ["Meetings"], "summary": "Submit a meeting quality sample", "security": [],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "description": "Requires a live participant token (`x-participant-id` + `x-participant-token`) or meeting admin token.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "mediaMode": { "type": "string", "enum": ["mesh", "sfu"] }, "quality": { "type": "string", "enum": ["good", "fair", "poor"] }, "peerCount": { "type": "integer" }, "rttMs": { "type": "number" }, "jitterMs": { "type": "number" }, "packetLossPct": { "type": "number" }, "bitrateUpBps": { "type": "integer" }, "bitrateDownBps": { "type": "integer" } } } } } },
        "responses": { "202": { "description": "Accepted" }, "403": { "description": "Participant token required" }, "404": { "description": "Meeting not found" } }
      },
      "get": {
        "tags": ["Meetings"], "summary": "Get recent meeting quality samples",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Quality summary and samples" }, "403": { "description": "Admin token required" }, "404": { "description": "Meeting not found" } }
      }
    },
    "/api/meetings/{meetingId}/egress": {
      "get": {
        "tags": ["SFU"], "summary": "Get LiveKit egress state",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Egress state" }, "403": { "description": "Admin token required" }, "404": { "description": "Meeting not found" } }
      }
    },
    "/api/meetings/{meetingId}/egress/start": {
      "post": {
        "tags": ["SFU"], "summary": "Start LiveKit room-composite egress",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string", "description": "rtmp://, rtmps://, or srt:// destination" } } } } } },
        "responses": { "201": { "description": "Egress started" }, "400": { "$ref": "#/components/responses/BadRequest" }, "403": { "description": "Admin token required" }, "409": { "description": "Stream already active" }, "503": { "description": "SFU not configured" } }
      }
    },
    "/api/meetings/{meetingId}/egress/stop": {
      "post": {
        "tags": ["SFU"], "summary": "Stop LiveKit room-composite egress",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Egress stopped" }, "403": { "description": "Admin token required" }, "404": { "description": "Meeting not found" } }
      }
    },

    "/api/meetings": {
      "post": {
        "tags": ["Meetings"], "summary": "Create a meeting (instant or scheduled)",
        "description": "Authenticated via API key or session cookie. Returns the joinUrl and an admin token used by the host.",
        "security": [{ "apiKey": [] }, { "session": [] }],
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateMeetingRequest" } } } },
        "responses": {
          "201": { "description": "Meeting created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateMeetingResponse" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "get": {
        "tags": ["Meetings"], "summary": "List active and scheduled meetings",
        "responses": { "200": { "description": "Active + scheduled", "content": { "application/json": { "schema": { "type": "object", "properties": { "active": { "type": "array", "items": { "$ref": "#/components/schemas/MeetingSummary" } }, "scheduled": { "type": "array", "items": { "$ref": "#/components/schemas/MeetingSummary" } } } } } } } }
      }
    },
    "/api/meetings/guest": {
      "post": {
        "tags": ["Guest Meetings"], "summary": "Create a guest meeting (no auth, rate-limited)", "security": [],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "maxLength": 100 }, "muteOnJoin": { "type": "boolean" }, "videoOffOnJoin": { "type": "boolean" } } } } } },
        "responses": { "201": { "description": "Guest meeting created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateMeetingResponse" } } } }, "403": { "description": "Guest meetings disabled" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/api/meetings/scheduled/list": {
      "get": {
        "tags": ["Meetings"], "summary": "List scheduled (not yet active) meetings",
        "responses": { "200": { "description": "Scheduled meetings", "content": { "application/json": { "schema": { "type": "object", "properties": { "scheduled": { "type": "array", "items": { "$ref": "#/components/schemas/MeetingSummary" } } } } } } } }
      }
    },
    "/api/meetings/{meetingId}": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "get": { "tags": ["Meetings"], "summary": "Get a meeting", "responses": { "200": { "description": "Meeting", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Meeting" } } } }, "404": { "$ref": "#/components/responses/NotFound" } } },
      "delete": { "tags": ["Meetings"], "summary": "End a meeting", "responses": { "200": { "description": "Meeting ended" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/meetings/{meetingId}/settings": {
      "patch": {
        "tags": ["Meetings"], "summary": "Update meeting settings (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MeetingSettings" } } } },
        "responses": { "200": { "description": "Settings updated" }, "403": { "$ref": "#/components/responses/Forbidden" } }
      }
    },

    "/api/meetings/{meetingId}/participants/{participantId}/mute": {
      "post": {
        "tags": ["Participants"], "summary": "Mute a participant (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "$ref": "#/components/parameters/ParticipantId" }],
        "responses": { "200": { "description": "Muted" } }
      }
    },
    "/api/meetings/{meetingId}/participants/{participantId}/unmute": {
      "post": {
        "tags": ["Participants"], "summary": "Unmute a participant (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "$ref": "#/components/parameters/ParticipantId" }],
        "responses": { "200": { "description": "Unmuted" } }
      }
    },
    "/api/meetings/{meetingId}/participants/{participantId}/kick": {
      "post": {
        "tags": ["Participants"], "summary": "Remove a participant (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "$ref": "#/components/parameters/ParticipantId" }],
        "responses": { "200": { "description": "Participant removed" } }
      }
    },
    "/api/meetings/{meetingId}/mute-all": {
      "post": {
        "tags": ["Participants"], "summary": "Mute all non-host participants (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "All muted" } }
      }
    },
    "/api/meetings/{meetingId}/invite": {
      "post": {
        "tags": ["Participants"], "summary": "Get the invite link (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Invite", "content": { "application/json": { "schema": { "type": "object", "properties": { "joinUrl": { "type": "string" } } } } } } }
      }
    },
    "/api/meetings/{meetingId}/lock": {
      "post": {
        "tags": ["Participants"], "summary": "Lock the meeting to new participants (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Locked" } }
      }
    },
    "/api/meetings/{meetingId}/unlock": {
      "post": {
        "tags": ["Participants"], "summary": "Unlock the meeting (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Unlocked" } }
      }
    },

    "/api/meetings/{meetingId}/polls": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "post": {
        "tags": ["Polls"], "summary": "Create a poll (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["question", "options"], "properties": { "question": { "type": "string", "maxLength": 500 }, "options": { "type": "array", "minItems": 2, "maxItems": 10, "items": { "type": "string", "maxLength": 200 } } } } } } },
        "responses": { "201": { "description": "Poll created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Poll" } } } }, "400": { "$ref": "#/components/responses/BadRequest" } }
      },
      "get": {
        "tags": ["Polls"], "summary": "List polls for a meeting (live + persisted)",
        "description": "Returns the in-memory polls for active meetings; for ended meetings, falls back to persisted poll history.",
        "responses": { "200": { "description": "Polls", "content": { "application/json": { "schema": { "type": "object", "properties": { "polls": { "type": "array", "items": { "$ref": "#/components/schemas/Poll" } } } } } } } }
      }
    },
    "/api/meetings/{meetingId}/polls/{pollId}/vote": {
      "post": {
        "tags": ["Polls"], "summary": "Vote in a poll",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "name": "pollId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["optionId"], "properties": { "optionId": { "type": "string" }, "participantId": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Vote recorded" }, "400": { "description": "Poll closed or invalid option" }, "404": { "description": "Poll not found" } }
      }
    },
    "/api/meetings/{meetingId}/polls/{pollId}/end": {
      "post": {
        "tags": ["Polls"], "summary": "End a poll and reveal results (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "name": "pollId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Results", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PollResults" } } } } }
      }
    },

    "/api/meetings/{meetingId}/questions": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "post": {
        "tags": ["Q&A"], "summary": "Submit a question",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["text"], "properties": { "text": { "type": "string", "maxLength": 500 }, "participantName": { "type": "string", "maxLength": 60 } } } } } },
        "responses": { "201": { "description": "Question accepted", "content": { "application/json": { "schema": { "type": "object", "properties": { "questionId": { "type": "string" } } } } } } }
      },
      "get": {
        "tags": ["Q&A"], "summary": "List questions (sorted by upvotes)",
        "responses": { "200": { "description": "Questions", "content": { "application/json": { "schema": { "type": "object", "properties": { "questions": { "type": "array", "items": { "$ref": "#/components/schemas/Question" } } } } } } } }
      }
    },
    "/api/meetings/{meetingId}/questions/{questionId}/upvote": {
      "post": {
        "tags": ["Q&A"], "summary": "Toggle an upvote on a question",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "name": "questionId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "participantId": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Upvote count", "content": { "application/json": { "schema": { "type": "object", "properties": { "upvoteCount": { "type": "integer" } } } } } } }
      }
    },
    "/api/meetings/{meetingId}/questions/{questionId}/answer": {
      "post": {
        "tags": ["Q&A"], "summary": "Mark a question answered (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "name": "questionId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "answer": { "type": "string", "maxLength": 1000 }, "answeredBy": { "type": "string", "maxLength": 60 } } } } } },
        "responses": { "200": { "description": "Marked answered" } }
      }
    },
    "/api/meetings/{meetingId}/questions/{questionId}/dismiss": {
      "post": {
        "tags": ["Q&A"], "summary": "Dismiss a question (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }, { "name": "questionId", "in": "path", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Dismissed" } }
      }
    },

    "/api/meetings/{meetingId}/notes": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "get": {
        "tags": ["Notes"], "summary": "Read collaborative meeting notes",
        "responses": { "200": { "description": "Notes content", "content": { "application/json": { "schema": { "type": "object", "properties": { "content": { "type": "string" }, "lastUpdatedBy": { "type": "string" }, "lastUpdatedAt": { "type": "integer", "format": "int64", "nullable": true } } } } } } }
      },
      "put": {
        "tags": ["Notes"], "summary": "Replace meeting notes (admin)", "security": [{ "apiKey": [], "adminToken": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["content"], "properties": { "content": { "type": "string", "maxLength": 50000 } } } } } },
        "responses": { "200": { "description": "Updated" } }
      }
    },

    "/api/meetings/{meetingId}/files": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "post": {
        "tags": ["Files"], "summary": "Upload a file to the meeting", "security": [{ "participantId": [], "participantToken": [] }, { "adminToken": [] }, { "apiKey": [] }, { "session": [] }],
        "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["file"], "properties": { "file": { "type": "string", "format": "binary" }, "participantName": { "type": "string", "maxLength": 60 } } } } } },
        "responses": {
          "201": { "description": "File stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/MeetingFile" } } } },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "description": "Per-meeting file limit reached (100)" }
        }
      },
      "get": {
        "tags": ["Files"], "summary": "List files shared in the meeting", "security": [{ "participantId": [], "participantToken": [] }, { "adminToken": [] }, { "apiKey": [] }, { "session": [] }],
        "responses": { "200": { "description": "Files", "content": { "application/json": { "schema": { "type": "object", "properties": { "files": { "type": "array", "items": { "$ref": "#/components/schemas/MeetingFile" } } } } } } } }
      }
    },
    "/api/meetings/files/{fileId}/download": {
      "get": {
        "tags": ["Files"], "summary": "Download a shared file", "security": [{ "participantId": [], "participantToken": [] }, { "adminToken": [] }, { "apiKey": [] }, { "session": [] }],
        "parameters": [{ "name": "fileId", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": {
          "200": { "description": "File stream", "content": { "application/octet-stream": {} } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/api/meetings/files/{fileId}": {
      "delete": {
        "tags": ["Files"], "summary": "Delete a shared file", "security": [{ "adminToken": [] }, { "apiKey": [] }, { "session": [] }],
        "parameters": [{ "name": "fileId", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": {
          "200": { "description": "File deleted" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },

    "/api/meetings/{meetingId}/attendance": {
      "get": {
        "tags": ["Attendance"], "summary": "Get attendance report (admin)", "security": [{ "adminToken": [] }, { "apiKey": [], "adminToken": [] }, { "session": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Attendance", "content": { "application/json": { "schema": { "type": "object", "properties": { "meetingId": { "type": "string" }, "title": { "type": "string" }, "attendance": { "type": "array", "items": { "$ref": "#/components/schemas/AttendanceRecord" } } } } } } } }
      }
    },
    "/api/meetings/{meetingId}/attendance/download": {
      "get": {
        "tags": ["Attendance"], "summary": "Download attendance as CSV (admin)", "security": [{ "adminToken": [] }, { "apiKey": [], "adminToken": [] }, { "session": [], "adminToken": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "CSV file", "content": { "text/csv": { "schema": { "type": "string" } } } } }
      }
    },

    "/api/templates": {
      "get": {
        "tags": ["Templates"], "summary": "List meeting templates",
        "responses": { "200": { "description": "Templates", "content": { "application/json": { "schema": { "type": "object", "properties": { "templates": { "type": "array", "items": { "$ref": "#/components/schemas/Template" } } } } } } } }
      },
      "post": {
        "tags": ["Templates"], "summary": "Create a meeting template",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "maxLength": 100 }, "title": { "type": "string", "maxLength": 100 }, "description": { "type": "string", "maxLength": 500 }, "settings": { "$ref": "#/components/schemas/MeetingSettings" } } } } } },
        "responses": { "201": { "description": "Template created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Template" } } } } }
      }
    },
    "/api/templates/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
      "put": {
        "tags": ["Templates"], "summary": "Update a meeting template", "security": [{ "session": [] }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "settings": { "$ref": "#/components/schemas/MeetingSettings" } } } } } },
        "responses": { "200": { "description": "Updated" }, "404": { "$ref": "#/components/responses/NotFound" } }
      },
      "delete": { "tags": ["Templates"], "summary": "Delete a meeting template", "responses": { "200": { "description": "Deleted" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },

    "/api/meetings/recurring": {
      "get": {
        "tags": ["Recurring"], "summary": "List simple-recurrence schedules",
        "responses": { "200": { "description": "Recurring meetings", "content": { "application/json": { "schema": { "type": "object", "properties": { "recurringMeetings": { "type": "array", "items": { "$ref": "#/components/schemas/RecurringMeeting" } } } } } } } }
      },
      "post": {
        "tags": ["Recurring"], "summary": "Create a simple-recurrence schedule",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["title", "recurrence", "timeUtc"], "properties": { "title": { "type": "string" }, "recurrence": { "type": "string", "enum": ["daily", "weekly", "biweekly", "monthly"] }, "dayOfWeek": { "type": "integer", "minimum": 0, "maximum": 6 }, "dayOfMonth": { "type": "integer", "minimum": 1, "maximum": 31 }, "timeUtc": { "type": "string", "example": "14:00" }, "timezone": { "type": "string", "example": "UTC" }, "settings": { "$ref": "#/components/schemas/MeetingSettings" } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RecurringMeeting" } } } } }
      }
    },
    "/api/meetings/recurring/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
      "put": { "tags": ["Recurring"], "summary": "Update a simple-recurrence schedule", "responses": { "200": { "description": "Updated" }, "404": { "$ref": "#/components/responses/NotFound" } } },
      "delete": { "tags": ["Recurring"], "summary": "Delete a simple-recurrence schedule", "responses": { "200": { "description": "Deleted" }, "404": { "$ref": "#/components/responses/NotFound" } } }
    },
    "/api/recurring": {
      "get": {
        "tags": ["Recurring"], "summary": "List RRULE-based recurring meetings",
        "responses": { "200": { "description": "Recurring", "content": { "application/json": { "schema": { "type": "object", "properties": { "recurring": { "type": "array", "items": { "$ref": "#/components/schemas/RruleRecurring" } } } } } } } }
      },
      "post": {
        "tags": ["Recurring"], "summary": "Create an RRULE-based recurring meeting",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["title", "rrule"], "properties": { "title": { "type": "string", "maxLength": 100 }, "rrule": { "type": "string", "example": "FREQ=WEEKLY;BYDAY=MO,WE;BYHOUR=14;BYMINUTE=0" }, "timezone": { "type": "string", "example": "UTC" }, "settings": { "$ref": "#/components/schemas/MeetingSettings" } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RruleRecurring" } } } } }
      }
    },
    "/api/recurring/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
      "patch": {
        "tags": ["Recurring"], "summary": "Activate / deactivate an RRULE schedule",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["is_active"], "properties": { "is_active": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" } }
      },
      "delete": { "tags": ["Recurring"], "summary": "Delete an RRULE schedule", "responses": { "200": { "description": "Deleted" } } }
    },

    "/api/meetings/history": {
      "get": {
        "tags": ["Meetings"], "summary": "Caller's meeting history (last 50)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "History", "content": { "application/json": { "schema": { "type": "object", "properties": { "meetings": { "type": "array", "items": { "$ref": "#/components/schemas/MeetingLogEntry" } } } } } } } }
      }
    },

    "/api/meetings/{meetingId}/transcript": {
      "get": {
        "tags": ["Transcripts"], "summary": "Get chat transcript JSON",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Messages", "content": { "application/json": { "schema": { "type": "object", "properties": { "messages": { "type": "array", "items": { "type": "object", "properties": { "participant_name": { "type": "string" }, "text": { "type": "string" }, "created_at": { "type": "string", "format": "date-time" } } } } } } } } }, "403": { "$ref": "#/components/responses/Forbidden" } }
      }
    },
    "/api/meetings/{meetingId}/transcript/download": {
      "get": {
        "tags": ["Transcripts"], "summary": "Download chat transcript as plain text",
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Plain-text transcript", "content": { "text/plain": { "schema": { "type": "string" } } } } }
      }
    },

    "/api/meetings/{meetingId}/recordings": {
      "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
      "post": {
        "tags": ["Recordings"], "summary": "Upload a recording (multipart, max 500MB)",
        "requestBody": { "required": true, "content": { "multipart/form-data": { "schema": { "type": "object", "required": ["recording"], "properties": { "recording": { "type": "string", "format": "binary" } } } } } },
        "responses": { "201": { "description": "Stored", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Recording" } } } } }
      },
      "get": {
        "tags": ["Recordings"], "summary": "List recordings for a meeting",
        "responses": { "200": { "description": "Recordings", "content": { "application/json": { "schema": { "type": "object", "properties": { "recordings": { "type": "array", "items": { "$ref": "#/components/schemas/Recording" } } } } } } } }
      }
    },
    "/api/recordings/{id}/download": {
      "get": {
        "tags": ["Recordings"], "summary": "Download a recording",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Stream", "content": { "application/octet-stream": {} } }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/api/recordings/{id}": {
      "delete": {
        "tags": ["Recordings"], "summary": "Delete a recording",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Deleted" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },

    "/api/webhooks": {
      "get": {
        "tags": ["Webhooks"], "summary": "List webhooks", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Webhooks", "content": { "application/json": { "schema": { "type": "object", "properties": { "webhooks": { "type": "array", "items": { "$ref": "#/components/schemas/Webhook" } } } } } } } }
      },
      "post": {
        "tags": ["Webhooks"], "summary": "Create a webhook (returns secret once)", "security": [{ "session": [] }],
        "description": "URL must be http/https and must not resolve to private/loopback/link-local addresses (SSRF protection). Allowed events: meeting.started, meeting.ended, participant.joined, participant.left, poll.created, poll.ended, breakout.started, breakout.ended.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url", "events"], "properties": { "url": { "type": "string", "format": "uri" }, "events": { "type": "array", "items": { "type": "string", "enum": ["meeting.started", "meeting.ended", "participant.joined", "participant.left", "poll.created", "poll.ended", "breakout.started", "breakout.ended"] } } } } } } },
        "responses": { "201": { "description": "Created with secret (only returned here)", "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/Webhook" }, { "type": "object", "properties": { "secret": { "type": "string" } } }] } } } }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },
    "/api/webhooks/{id}": {
      "delete": {
        "tags": ["Webhooks"], "summary": "Delete a webhook", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Deleted" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },

    "/api/billing/stripe/checkout": {
      "post": {
        "tags": ["Billing"], "summary": "Create a Stripe checkout session for credit top-up", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["amountUsd"], "properties": { "amountUsd": { "type": "number", "minimum": 5, "maximum": 1000 } } } } } },
        "responses": { "200": { "description": "Checkout URL", "content": { "application/json": { "schema": { "type": "object", "properties": { "url": { "type": "string", "format": "uri" } } } } } }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },
    "/api/billing/stripe/webhook": {
      "post": {
        "tags": ["Billing"], "summary": "Stripe webhook callback (signed)", "security": [],
        "description": "Receives `checkout.session.completed` events from Stripe. Body is raw JSON; signature verified via `stripe-signature` header.",
        "responses": { "200": { "description": "Event acknowledged" }, "400": { "description": "Invalid signature" } }
      }
    },
    "/api/billing/usdc/address": {
      "get": {
        "tags": ["Billing"], "summary": "Get the caller's USDC deposit address (HD-derived)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Address", "content": { "application/json": { "schema": { "type": "object", "properties": { "address": { "type": "string", "nullable": true } } } } } } }
      }
    },
    "/api/billing/history": {
      "get": {
        "tags": ["Billing"], "summary": "Credit balance + 50 most recent transactions", "security": [{ "session": [] }],
        "responses": { "200": { "description": "History", "content": { "application/json": { "schema": { "type": "object", "properties": { "balance": { "type": "number" }, "transactions": { "type": "array", "items": { "$ref": "#/components/schemas/CreditTransaction" } } } } } } } }
      }
    },

    "/api/company/join": {
      "post": {
        "tags": ["Company"], "summary": "Join a company with an invite code", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["inviteCode"], "properties": { "inviteCode": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Joined" }, "400": { "description": "Already in a company" }, "404": { "description": "Invalid invite code" } }
      }
    },
    "/api/company/members": {
      "get": {
        "tags": ["Company"], "summary": "List company members", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Members", "content": { "application/json": { "schema": { "type": "object", "properties": { "isOwner": { "type": "boolean" }, "members": { "type": "array", "items": { "$ref": "#/components/schemas/CompanyMember" } } } } } } } }
      }
    },
    "/api/company/members/{id}": {
      "delete": {
        "tags": ["Company"], "summary": "Remove a company member (owner only)", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Removed" }, "403": { "description": "Only owner can remove members" } }
      }
    },

    "/health": {
      "get": {
        "tags": ["Health"], "summary": "Full health check", "security": [],
        "responses": { "200": { "description": "Process and meeting status", "content": { "application/json": { "schema": { "type": "object", "properties": { "status": { "type": "string", "enum": ["ok"] }, "uptime": { "type": "number" }, "timestamp": { "type": "string", "format": "date-time" }, "meetings": { "type": "integer" } } } } } }, "503": { "description": "Database unavailable" } }
      }
    },
    "/health/liveness": {
      "get": { "tags": ["Health"], "summary": "Liveness probe", "security": [], "responses": { "200": { "description": "Process is running" } } }
    },
    "/health/readiness": {
      "get": { "tags": ["Health"], "summary": "Readiness probe (checks DB)", "security": [], "responses": { "200": { "description": "Database connected" }, "503": { "description": "Database unavailable" } } }
    },
    "/metrics": {
      "get": {
        "tags": ["Health"], "summary": "Prometheus text-format metrics", "security": [],
        "responses": { "200": { "description": "Metrics text" }, "401": { "description": "Unauthorized" }, "404": { "description": "Metrics disabled in production" } }
      }
    },

    "/admin/login": {
      "post": {
        "tags": ["Admin: Users"], "summary": "Admin sign-in", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "password"], "properties": { "email": { "type": "string", "format": "email" }, "password": { "type": "string" }, "mfaCode": { "type": "string", "description": "Required when global admin MFA is configured or the admin account has per-admin MFA enabled. Backup codes are accepted for per-admin MFA." } } } } } },
        "responses": { "200": { "description": "Logged in" }, "401": { "description": "Invalid credentials" }, "403": { "description": "Account disabled" }, "429": { "$ref": "#/components/responses/RateLimited" } }
      }
    },
    "/admin/logout": {
      "post": { "tags": ["Admin: Users"], "summary": "Admin sign-out", "security": [{ "session": [] }], "responses": { "302": { "description": "Redirect to /admin" } } }
    },
    "/admin/api/mfa": {
      "get": {
        "tags": ["Admin: Users"], "summary": "Get current admin MFA status", "security": [{ "session": [] }],
        "responses": { "200": { "description": "MFA status", "content": { "application/json": { "schema": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "envPolicyEnabled": { "type": "boolean" }, "backupCodeCount": { "type": "integer" } } } } } } }
      }
    },
    "/admin/api/mfa/setup": {
      "post": {
        "tags": ["Admin: Users"], "summary": "Start per-admin MFA setup", "security": [{ "session": [] }],
        "responses": { "200": { "description": "TOTP setup secret and otpauth URL" }, "500": { "description": "Failed to start MFA setup" } }
      }
    },
    "/admin/api/mfa/enable": {
      "post": {
        "tags": ["Admin: Users"], "summary": "Enable per-admin MFA after verifying TOTP", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["code"], "properties": { "code": { "type": "string" } } } } } },
        "responses": { "200": { "description": "MFA enabled with one-time backup codes" }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },
    "/admin/api/mfa/backup-codes": {
      "post": {
        "tags": ["Admin: Users"], "summary": "Regenerate per-admin MFA backup codes", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["code"], "properties": { "code": { "type": "string" } } } } } },
        "responses": { "200": { "description": "New backup codes" }, "401": { "description": "Invalid MFA code" } }
      }
    },
    "/admin/api/mfa/disable": {
      "post": {
        "tags": ["Admin: Users"], "summary": "Disable per-admin MFA", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["password"], "properties": { "password": { "type": "string" }, "code": { "type": "string" } } } } } },
        "responses": { "200": { "description": "MFA disabled" }, "401": { "description": "Invalid password or MFA code" } }
      }
    },
    "/admin/api/stats": {
      "get": {
        "tags": ["Admin: Users"], "summary": "Top-line counters", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Stats", "content": { "application/json": { "schema": { "type": "object", "properties": { "activeMeetings": { "type": "integer" }, "totalUsers": { "type": "integer" }, "activeApiKeys": { "type": "integer" }, "totalCompanies": { "type": "integer" }, "totalCreditsIssued": { "type": "number" } } } } } } }
      }
    },
    "/admin/api/meetings": {
      "get": {
        "tags": ["Admin: Meetings"], "summary": "List active meetings (paged)", "security": [{ "session": [] }],
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 } },
          { "name": "offset", "in": "query", "schema": { "type": "integer", "minimum": 0, "default": 0 } }
        ],
        "responses": { "200": { "description": "Active meetings + total", "content": { "application/json": { "schema": { "type": "object", "properties": { "total": { "type": "integer" }, "meetings": { "type": "array", "items": { "$ref": "#/components/schemas/Meeting" } } } } } } } }
      }
    },
    "/admin/api/meetings/snapshots": {
      "get": {
        "tags": ["Admin: Meetings"], "summary": "List externalized live meeting snapshots", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Live meeting snapshots mirrored to Postgres" } }
      }
    },
    "/admin/api/meetings/{meetingId}": {
      "delete": {
        "tags": ["Admin: Meetings"], "summary": "End a live meeting", "security": [{ "session": [] }],
        "parameters": [{ "$ref": "#/components/parameters/MeetingId" }],
        "responses": { "200": { "description": "Ended" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/admin/api/meetings/history": {
      "get": {
        "tags": ["Admin: Meetings"], "summary": "Meeting history across all users (last 100)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "History", "content": { "application/json": { "schema": { "type": "object", "properties": { "meetings": { "type": "array", "items": { "allOf": [{ "$ref": "#/components/schemas/MeetingLogEntry" }, { "type": "object", "properties": { "user_email": { "type": "string" }, "company_name": { "type": "string", "nullable": true } } }] } } } } } } } }
      }
    },
    "/admin/api/users": {
      "get": {
        "tags": ["Admin: Users"], "summary": "List users (last 200)", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Users", "content": { "application/json": { "schema": { "type": "object", "properties": { "users": { "type": "array", "items": { "$ref": "#/components/schemas/AdminUser" } } } } } } } }
      }
    },
    "/admin/api/users/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
      "patch": {
        "tags": ["Admin: Users"], "summary": "Toggle user is_active / is_admin", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "isActive": { "type": "boolean" }, "isAdmin": { "type": "boolean" } } } } } },
        "responses": { "200": { "description": "Updated" }, "400": { "$ref": "#/components/responses/BadRequest" } }
      },
      "delete": { "tags": ["Admin: Users"], "summary": "Delete a user", "security": [{ "session": [] }], "responses": { "200": { "description": "Deleted" }, "400": { "description": "Cannot delete own account" } } }
    },
    "/admin/api/keys": {
      "get": {
        "tags": ["Admin: Keys"], "summary": "List recent API keys", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Keys", "content": { "application/json": { "schema": { "type": "object", "properties": { "keys": { "type": "array", "items": { "$ref": "#/components/schemas/AdminApiKey" } } } } } } } }
      },
      "post": {
        "tags": ["Admin: Keys"], "summary": "Create an API key for a user", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["userId"], "properties": { "userId": { "type": "integer" }, "label": { "type": "string" } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiKey" } } } } }
      }
    },
    "/admin/api/keys/{id}": {
      "delete": {
        "tags": ["Admin: Keys"], "summary": "Revoke a key", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Revoked" } }
      }
    },
    "/admin/api/settings": {
      "get": { "tags": ["Admin: Settings"], "summary": "Read all settings", "security": [{ "session": [] }], "responses": { "200": { "description": "Settings map", "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } } },
      "patch": {
        "tags": ["Admin: Settings"], "summary": "Update one or more settings", "security": [{ "session": [] }],
        "description": "Allowed boolean keys: recording_enabled, screen_share_enabled, blur_enabled, registration_enabled, stripe_enabled, crypto_enabled, captions_enabled, guest_meetings_enabled, polls_enabled, qa_enabled, file_sharing_enabled, meeting_notes_enabled, breakout_rooms_enabled, live_streaming_enabled, e2ee_enabled, spatial_audio_enabled, network_stats_enabled. Integer keys: max_participants_default, meeting_auto_delete_minutes, free_tier_max_participants, free_tier_max_duration_minutes, content_retention_days. Decimal keys: meeting_cost_per_participant_minute, low_balance_threshold_usd.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "additionalProperties": { "oneOf": [{ "type": "string" }, { "type": "number" }, { "type": "boolean" }] } } } } },
        "responses": { "200": { "description": "Updated" }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },
    "/admin/api/billing/transactions": {
      "get": { "tags": ["Admin: Billing"], "summary": "Last 100 credit transactions", "security": [{ "session": [] }], "responses": { "200": { "description": "Transactions" } } }
    },
    "/admin/api/billing/stripe": {
      "get": { "tags": ["Admin: Billing"], "summary": "All Stripe top-up sessions", "security": [{ "session": [] }], "responses": { "200": { "description": "Top-ups" } } }
    },
    "/admin/api/billing/credit": {
      "post": {
        "tags": ["Admin: Billing"], "summary": "Manually credit a user / company", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["email", "amountUsd"], "properties": { "email": { "type": "string", "format": "email" }, "amountUsd": { "type": "number", "exclusiveMinimum": 0 }, "note": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Credit applied" }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "description": "User not found" } }
      }
    },
    "/admin/api/companies": {
      "get": { "tags": ["Admin: Companies"], "summary": "List companies (last 200)", "security": [{ "session": [] }], "responses": { "200": { "description": "Companies" } } }
    },
    "/admin/api/companies/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
      "patch": {
        "tags": ["Admin: Companies"], "summary": "Change a company's plan", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["plan"], "properties": { "plan": { "type": "string", "enum": ["free", "starter", "pro", "business"] } } } } } },
        "responses": { "200": { "description": "Updated" }, "404": { "$ref": "#/components/responses/NotFound" } }
      },
      "delete": { "tags": ["Admin: Companies"], "summary": "Dissolve a company (nullifies FKs)", "security": [{ "session": [] }], "responses": { "200": { "description": "Dissolved" } } }
    },
    "/admin/api/crypto/config": {
      "get": {
        "tags": ["Admin: Crypto"], "summary": "Read USDC monitor config", "security": [{ "session": [] }],
        "responses": { "200": { "description": "Config", "content": { "application/json": { "schema": { "type": "object", "properties": { "platformWallet": { "type": "string" }, "rpcUrl": { "type": "string" }, "lastBlock": { "type": "string" }, "monitorActive": { "type": "boolean" } } } } } } }
      }
    },
    "/admin/api/crypto/wallet": {
      "put": {
        "tags": ["Admin: Crypto"], "summary": "Set platform receiving wallet", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["address"], "properties": { "address": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" } } } } } },
        "responses": { "200": { "description": "Updated" }, "400": { "description": "Invalid address" } }
      }
    },
    "/admin/api/crypto/rpc": {
      "put": {
        "tags": ["Admin: Crypto"], "summary": "Set RPC URL", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url"], "properties": { "url": { "type": "string", "format": "uri" } } } } } },
        "responses": { "200": { "description": "Updated" }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },
    "/admin/api/crypto/deposits": {
      "get": { "tags": ["Admin: Crypto"], "summary": "List USDC deposits", "security": [{ "session": [] }], "responses": { "200": { "description": "Deposits" } } }
    },
    "/admin/api/crypto/deposits/{id}/confirm": {
      "patch": {
        "tags": ["Admin: Crypto"], "summary": "Manually confirm a pending deposit", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "responses": { "200": { "description": "Confirmed" }, "404": { "description": "Not found or already confirmed" } }
      }
    },
    "/admin/api/crypto/unmatched": {
      "get": {
        "tags": ["Admin: Crypto"], "summary": "List unmatched USDC transfers", "security": [{ "session": [] }],
        "parameters": [{ "name": "resolved", "in": "query", "schema": { "type": "string", "enum": ["true", "false"] } }],
        "responses": { "200": { "description": "Transfers" } }
      }
    },
    "/admin/api/crypto/unmatched/{id}": {
      "patch": {
        "tags": ["Admin: Crypto"], "summary": "Mark an unmatched transfer resolved/unresolved", "security": [{ "session": [] }],
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "resolved": { "type": "boolean" }, "note": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Updated" }, "404": { "$ref": "#/components/responses/NotFound" } }
      }
    },
    "/admin/api/crypto/rescan": {
      "post": {
        "tags": ["Admin: Crypto"], "summary": "Reset the monitor checkpoint to a block height", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["fromBlock"], "properties": { "fromBlock": { "type": "integer", "minimum": 0 } } } } } },
        "responses": { "200": { "description": "Updated" }, "400": { "$ref": "#/components/responses/BadRequest" } }
      }
    },

    "/admin/api/analytics/overview": {
      "get": { "tags": ["Admin: Analytics"], "summary": "Top-line analytics totals", "security": [{ "session": [] }], "responses": { "200": { "description": "Overview" } } }
    },
    "/admin/api/analytics/events": {
      "get": {
        "tags": ["Admin: Analytics"], "summary": "Event-type breakdown", "security": [{ "session": [] }],
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 } }],
        "responses": { "200": { "description": "Events grouped by type" } }
      }
    },
    "/admin/api/analytics/trends": {
      "get": {
        "tags": ["Admin: Analytics"], "summary": "Daily meeting / participant / revenue trends", "security": [{ "session": [] }],
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 } }],
        "responses": { "200": { "description": "Daily trends" } }
      }
    },
    "/admin/api/analytics/users": {
      "get": { "tags": ["Admin: Analytics"], "summary": "Top users by activity", "security": [{ "session": [] }], "responses": { "200": { "description": "User leaderboard" } } }
    },
    "/admin/api/analytics/ai": {
      "get": { "tags": ["Admin: Analytics"], "summary": "AI usage by model / module / day", "security": [{ "session": [] }], "responses": { "200": { "description": "AI usage" } } }
    },
    "/admin/api/analytics/support-key": {
      "post": {
        "tags": ["Admin: Analytics"], "summary": "Resolve a support key to a user (single-use)", "security": [{ "session": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["key"], "properties": { "key": { "type": "string", "minLength": 32, "maxLength": 32 } } } } } },
        "responses": { "200": { "description": "User + recent meetings/events" }, "400": { "$ref": "#/components/responses/BadRequest" }, "404": { "description": "Not found / expired / already used" } }
      }
    },
    "/admin/api/analytics/features": {
      "get": {
        "tags": ["Admin: Analytics"], "summary": "Feature usage counts", "security": [{ "session": [] }],
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 } }],
        "responses": { "200": { "description": "Feature usage" } }
      }
    },
    "/admin/api/analytics/errors": {
      "get": {
        "tags": ["Admin: Analytics"], "summary": "Aggregated error events", "security": [{ "session": [] }],
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 30 } }],
        "responses": { "200": { "description": "Errors" } }
      }
    },
    "/admin/api/analytics/realtime": {
      "get": { "tags": ["Admin: Analytics"], "summary": "Live realtime counters + top 20 meetings", "security": [{ "session": [] }], "responses": { "200": { "description": "Realtime" } } }
    },
    "/admin/api/analytics/retention": {
      "get": { "tags": ["Admin: Analytics"], "summary": "Weekly user retention (last 12 weeks)", "security": [{ "session": [] }], "responses": { "200": { "description": "Retention" } } }
    },
    "/admin/api/analytics/health": {
      "get": { "tags": ["Admin: Analytics"], "summary": "Process health (memory, DB pool, meetings)", "security": [{ "session": [] }], "responses": { "200": { "description": "Health" } } }
    },
    "/admin/api/analytics/peak-hours": {
      "get": {
        "tags": ["Admin: Analytics"], "summary": "Heatmap of meetings by day-of-week / hour", "security": [{ "session": [] }],
        "parameters": [{ "name": "days", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 365, "default": 90 } }],
        "responses": { "200": { "description": "Heatmap" } }
      }
    },
    "/api/auth/csrf": {
      "get": {
        "tags": ["Auth"],
        "summary": "Get CSRF token",
        "description": "Returns a CSRF token for authenticated browser-session mutation requests. Same-origin HTML pages inject this automatically.",
        "security": [{ "session": [] }],
        "responses": {
          "200": {
            "description": "CSRF token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "csrfToken": { "type": "string" } },
                  "required": ["csrfToken"]
                }
              }
            }
          }
        }
      }
    }
  },

  "components": {
    "securitySchemes": {
      "apiKey":     { "type": "apiKey", "in": "header", "name": "x-api-key", "description": "API key from Dashboard → API Keys" },
      "adminToken": { "type": "apiKey", "in": "header", "name": "x-admin-token", "description": "Admin token returned when a meeting is created" },
      "participantId": { "type": "apiKey", "in": "header", "name": "x-participant-id", "description": "Live meeting participant ID returned by the joined Socket.IO event" },
      "participantToken": { "type": "apiKey", "in": "header", "name": "x-participant-token", "description": "Private live participant token returned by the joined Socket.IO event" },
      "session":    { "type": "apiKey", "in": "cookie", "name": "connect.sid", "description": "Session cookie (set on login)" }
    },
    "parameters": {
      "MeetingId": { "name": "meetingId", "in": "path", "required": true, "schema": { "type": "string" }, "description": "Meeting identifier (e.g. abc-defg-hij)" },
      "ParticipantId": { "name": "participantId", "in": "path", "required": true, "schema": { "type": "string" } }
    },
    "responses": {
      "BadRequest":   { "description": "Validation error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "Authentication required", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden":    { "description": "Caller is authenticated but not authorized", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound":     { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited":  { "description": "Too many requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "properties": { "error": { "type": "string" } } },
      "RegisterRequest": {
        "type": "object", "required": ["email", "password"],
        "properties": {
          "email":       { "type": "string", "format": "email", "maxLength": 255 },
          "password":    { "type": "string", "minLength": 8, "maxLength": 128 },
          "accountType": { "type": "string", "enum": ["personal", "company"], "default": "personal" },
          "companyName": { "type": "string", "maxLength": 100, "description": "Required when accountType=company" }
        }
      },
      "RegisterResponse": {
        "type": "object",
        "properties": {
          "message":     { "type": "string" },
          "email":       { "type": "string" },
          "apiKey":      { "type": "string", "description": "Auto-issued default API key" },
          "inviteCode":  { "type": "string", "nullable": true, "description": "Company invite code (company accounts only)" },
          "accountType": { "type": "string" }
        }
      },
      "UserProfile": {
        "type": "object",
        "properties": {
          "id":              { "type": "integer" },
          "email":           { "type": "string" },
          "is_admin":        { "type": "boolean" },
          "account_type":    { "type": "string" },
          "company_id":      { "type": "integer", "nullable": true },
          "credits_usd":     { "type": "number" },
          "created_at":      { "type": "string", "format": "date-time" },
          "company_name":    { "type": "string", "nullable": true },
          "company_credits": { "type": "number", "nullable": true },
          "invite_code":     { "type": "string", "nullable": true },
          "plan":            { "type": "string", "nullable": true }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id":           { "type": "integer" },
          "key":          { "type": "string", "nullable": true, "description": "Full secret. Present only in create/register responses; null in list responses." },
          "keyPreview":   { "type": "string", "description": "Non-secret display preview for listed keys" },
          "legacy_secret_stored": { "type": "boolean", "description": "True for migrated rows that still have a legacy plaintext value stored server-side" },
          "label":        { "type": "string" },
          "is_active":    { "type": "boolean" },
          "created_at":   { "type": "string", "format": "date-time" },
          "last_used_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "AdminApiKey": {
        "allOf": [
          { "$ref": "#/components/schemas/ApiKey" },
          { "type": "object", "properties": { "user_email": { "type": "string" } } }
        ]
      },
      "AdminUser": {
        "type": "object",
        "properties": {
          "id":              { "type": "integer" },
          "email":           { "type": "string" },
          "is_admin":        { "type": "boolean" },
          "is_active":       { "type": "boolean" },
          "credits_usd":     { "type": "number" },
          "account_type":    { "type": "string" },
          "company_name":    { "type": "string", "nullable": true },
          "active_key_count":{ "type": "integer" },
          "created_at":      { "type": "string", "format": "date-time" }
        }
      },
      "MeetingSettings": {
        "type": "object",
        "properties": {
          "muteOnJoin":      { "type": "boolean" },
          "videoOffOnJoin":  { "type": "boolean" },
          "maxParticipants": { "type": "integer", "minimum": 2, "maximum": 500 },
          "locked":          { "type": "boolean" },
          "waitingRoom":     { "type": "boolean" }
        }
      },
      "CreateMeetingRequest": {
        "type": "object",
        "properties": {
          "title":           { "type": "string", "maxLength": 100 },
          "muteOnJoin":      { "type": "boolean" },
          "videoOffOnJoin":  { "type": "boolean" },
          "maxParticipants": { "type": "integer", "minimum": 2, "maximum": 500 },
          "waitingRoom":     { "type": "boolean" },
          "scheduledAt":     { "type": "string", "format": "date-time", "description": "ISO 8601 future timestamp; if set, the meeting is created in scheduled state" },
          "templateId":      { "type": "integer", "description": "Apply settings/title from a template" }
        }
      },
      "CreateMeetingResponse": {
        "type": "object",
        "properties": {
          "meetingId":   { "type": "string" },
          "adminToken":  { "type": "string", "description": "Send as `x-admin-token` for host-only operations" },
          "joinUrl":     { "type": "string" },
          "title":       { "type": "string" },
          "status":      { "type": "string", "enum": ["active", "scheduled"] },
          "scheduledAt": { "type": "string", "format": "date-time" },
          "settings":    { "$ref": "#/components/schemas/MeetingSettings" }
        }
      },
      "MeetingSummary": {
        "type": "object",
        "properties": {
          "meetingId":        { "type": "string" },
          "title":            { "type": "string" },
          "createdAt":        { "type": "integer", "format": "int64" },
          "status":           { "type": "string", "enum": ["active", "scheduled"] },
          "participantCount": { "type": "integer" },
          "scheduledAt":      { "type": "string", "format": "date-time" }
        }
      },
      "Meeting": {
        "type": "object",
        "properties": {
          "meetingId":        { "type": "string" },
          "title":            { "type": "string" },
          "createdAt":        { "type": "integer", "format": "int64" },
          "participantCount": { "type": "integer" },
          "participants":     { "type": "array", "items": { "$ref": "#/components/schemas/Participant" } },
          "settings":         { "$ref": "#/components/schemas/MeetingSettings" }
        }
      },
      "Participant": {
        "type": "object",
        "properties": {
          "participantId": { "type": "string" },
          "name":          { "type": "string" },
          "isMuted":       { "type": "boolean" },
          "isVideoOff":    { "type": "boolean" }
        }
      },
      "MeetingLogEntry": {
        "type": "object",
        "properties": {
          "meeting_id":        { "type": "string" },
          "title":             { "type": "string" },
          "started_at":        { "type": "string", "format": "date-time" },
          "ended_at":          { "type": "string", "format": "date-time", "nullable": true },
          "peak_participants": { "type": "integer" },
          "duration_minutes":  { "type": "number" },
          "cost_usd":          { "type": "number" }
        }
      },
      "Poll": {
        "type": "object",
        "properties": {
          "id":         { "type": "string" },
          "question":   { "type": "string" },
          "options":    { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "text": { "type": "string" }, "count": { "type": "integer" } } } },
          "isActive":   { "type": "boolean" },
          "totalVotes": { "type": "integer" }
        }
      },
      "PollResults": {
        "type": "object",
        "properties": {
          "pollId":     { "type": "string" },
          "results":    { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "text": { "type": "string" }, "count": { "type": "integer" } } } },
          "totalVotes": { "type": "integer" }
        }
      },
      "Question": {
        "type": "object",
        "properties": {
          "id":          { "type": "string" },
          "text":        { "type": "string" },
          "askedBy":     { "type": "object", "properties": { "name": { "type": "string" } } },
          "upvoteCount": { "type": "integer" },
          "isAnswered":  { "type": "boolean" },
          "answer":      { "type": "string", "nullable": true },
          "answeredBy":  { "type": "string", "nullable": true },
          "createdAt":   { "type": "integer", "format": "int64" }
        }
      },
      "MeetingFile": {
        "type": "object",
        "properties": {
          "id":               { "type": "integer" },
          "original_name":    { "type": "string" },
          "size_bytes":       { "type": "integer" },
          "mime_type":        { "type": "string" },
          "participant_name": { "type": "string" },
          "created_at":       { "type": "string", "format": "date-time" }
        }
      },
      "AttendanceRecord": {
        "type": "object",
        "properties": {
          "participantId":   { "type": "string" },
          "name":            { "type": "string" },
          "joinedAt":        { "type": "string", "format": "date-time" },
          "leftAt":          { "type": "string", "format": "date-time", "nullable": true },
          "durationSeconds": { "type": "integer" }
        }
      },
      "Template": {
        "type": "object",
        "properties": {
          "id":          { "type": "integer" },
          "name":        { "type": "string" },
          "title":       { "type": "string" },
          "description": { "type": "string" },
          "settings":    { "$ref": "#/components/schemas/MeetingSettings" },
          "is_default":  { "type": "boolean" },
          "created_at":  { "type": "string", "format": "date-time" }
        }
      },
      "RecurringMeeting": {
        "type": "object",
        "properties": {
          "id":               { "type": "integer" },
          "title":            { "type": "string" },
          "recurrence":       { "type": "string", "enum": ["daily", "weekly", "biweekly", "monthly"] },
          "day_of_week":      { "type": "integer", "nullable": true, "minimum": 0, "maximum": 6 },
          "day_of_month":     { "type": "integer", "nullable": true, "minimum": 1, "maximum": 31 },
          "time_utc":         { "type": "string" },
          "timezone":         { "type": "string" },
          "settings":         { "$ref": "#/components/schemas/MeetingSettings" },
          "meeting_id":       { "type": "string" },
          "admin_token":      { "type": "string" },
          "is_active":        { "type": "boolean" },
          "next_occurrence":  { "type": "string", "format": "date-time" },
          "joinUrl":          { "type": "string" },
          "created_at":       { "type": "string", "format": "date-time" }
        }
      },
      "RruleRecurring": {
        "type": "object",
        "properties": {
          "id":          { "type": "integer" },
          "title":       { "type": "string" },
          "settings":    { "$ref": "#/components/schemas/MeetingSettings" },
          "rrule":       { "type": "string" },
          "timezone":    { "type": "string" },
          "next_run_at": { "type": "string", "format": "date-time", "nullable": true },
          "is_active":   { "type": "boolean" },
          "created_at":  { "type": "string", "format": "date-time" }
        }
      },
      "Recording": {
        "type": "object",
        "properties": {
          "id":         { "type": "integer" },
          "filename":   { "type": "string" },
          "size_bytes": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id":         { "type": "integer" },
          "url":        { "type": "string", "format": "uri" },
          "events":     { "type": "array", "items": { "type": "string" } },
          "is_active":  { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "CreditTransaction": {
        "type": "object",
        "properties": {
          "id":           { "type": "integer" },
          "user_id":      { "type": "integer", "nullable": true },
          "company_id":   { "type": "integer", "nullable": true },
          "amount_usd":   { "type": "number" },
          "type":         { "type": "string", "description": "e.g. stripe_topup, usdc_deposit, meeting_charge, admin_grant" },
          "reference_id": { "type": "string", "nullable": true },
          "description":  { "type": "string", "nullable": true },
          "created_at":   { "type": "string", "format": "date-time" }
        }
      },
      "CompanyMember": {
        "type": "object",
        "properties": {
          "id":           { "type": "integer" },
          "email":        { "type": "string" },
          "account_type": { "type": "string" },
          "credits_used": { "type": "number" },
          "created_at":   { "type": "string", "format": "date-time" }
        }
      }
    }
  }
}
