{
  "openapi": "3.1.0",
  "info": {
    "title": "GoGuardian Extension REST API",
    "version": "4.0.7422.1",
    "description": "Complete REST/HTTP surface of the GoGuardian student-monitoring Chrome extension, reconstructed from its minified webpack bundles (v4.0.7422.1). Every fact below is verified against the reconstructed source tree: request/response field names, content types, schedules, retry policies and evidence citations come from the wire-level extraction pass (module numbers refer to files under source/background/src/ and source/background/raw-modules/).\nAUTH MODEL — the generic HTTP client (module 149, base https://extapi.goguardian.com) sets on every request: \"Authorization\" = the raw compRand token (NO \"Bearer\" prefix; the token is issued by POST /api/v1/ext/register and stored in localStorage.compRand), and \"ExtensionVersion\" = the running extension version. When present and parseable, a \"Device-Id\" header (localStorage.enterpriseDirectoryDeviceId) is also sent. A few hand-rolled clients (beacon predictors, lesson-plan, Twilio) send lowercase \"authorization\"/\"extensionversion\" variants.\nHOST OVERRIDE QUIRK — the server can repoint any path to a different host via an apiMap (delivered with privacy settings): ajax() checks apiMap[fullPathString] before falling back to the default base, keyed by the complete path string, not a prefix.\nENCODING — module-149 POST/PUT calls that pass a plain data object are jQuery form-encoded (application/x-www-form-urlencoded) by default; only calls that explicitly set contentType send JSON. Binary pipelines gzip an Avro record (entity batch, statsd) or a protobuf message (roll-call, apps-exts) and post it as application/binary or application/octet-stream.\nHONEST CAVEAT — response schemas list only the fields the client code actually reads; servers return more. Entries marked \"response not read\" are fire-and-forget. Uncertainties from the extraction pass are called out inline."
  },
  "servers": [
    {
      "url": "https://extapi.goguardian.com",
      "description": "Default base for the module-149 HTTP client. All paths on other hosts carry their own path-level servers."
    }
  ],
  "security": [
    {
      "CompRand": [],
      "ExtensionVersion": []
    }
  ],
  "tags": [
    {
      "name": "Registration & Identity",
      "description": "Bootstrap registration, account/user state, name/email identity."
    },
    {
      "name": "Settings & Policy",
      "description": "Privacy, admin policy, experiments, notifications, model sync."
    },
    {
      "name": "Surveillance",
      "description": "Entity batch (Avro/gzip), roll-call heartbeat, screenshots."
    },
    {
      "name": "Telemetry",
      "description": "StatsD metrics and log shipping (Avro / JSON to countvoncount)."
    },
    {
      "name": "Smart Alerts (X3)",
      "description": "Self-harm content prediction and report pipeline (x3-*)."
    },
    {
      "name": "Beacon (crisis response)",
      "description": "Fullpage/search predictors and beacon reports shown to students."
    },
    {
      "name": "Classroom & Calls",
      "description": "Chat messages, lesson plans, video-call participants."
    },
    {
      "name": "Taskiness & Video",
      "description": "Off-task scoring and YouTube video metadata."
    },
    {
      "name": "Pusher Auth",
      "description": "Channel authorization for the realtime Pusher connection."
    },
    {
      "name": "Utilities",
      "description": "Public IP, clock sync, native-agent liveness, diagnostics."
    },
    {
      "name": "Block Pages",
      "description": "Navigations to blocked.goguardian.com with signed context params."
    },
    {
      "name": "S3 Upload",
      "description": "Server-signed multipart uploads to an S3 bucket."
    }
  ],
  "paths": {
    "/api/v1/ext/register": {
      "post": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "register",
        "summary": "Exchange license-extension IDs for org identity and compRand token",
        "description": "First call of the bootstrap. The extension lists installed extensions (chrome.management), filters for those named exactly \"Chromium License\" or \"GoGuardian License\", and POSTs their extension IDs as orgRands (also stored to localStorage.orgRand). The server maps them to the org and returns the register payload merged verbatim into state.register. Retried 1 s base delay, 8 attempts. Failure path: \"No License, please install license extension.\" -> Register subsystem wipes localStorage.uuid and reloads the extension after 60 s.",
        "x-evidence": "raw-modules/1403.js:23-51, raw-modules/1337.js:19-40, src/1519-register.js:93-101",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Register payload merged into state.register (reducer 1337).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegisterResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/version": {
      "put": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "reportVersion",
        "summary": "Report a running version newer than the server's record",
        "description": "Sent only when the running version compares greater than user.extVer from GET /api/v1/ext/user. Failure is logged and non-fatal.",
        "x-evidence": "src/1521-user.js:149-154",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/VersionRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/api/v1/ext/bypass": {
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "validateBypassPassword",
        "summary": "Validate a block-page bypass password",
        "description": "Called by the background Bypass subsystem when the student submits the block-page bypass form (admin bundle UI opens a runtime port; the background performs the call). On success the server returns a bypass window (time in minutes; bypassEnd = now + 60*time) and the tab is navigated to the target URL. CORRECTION of the earlier markdown doc (source/docs/WEB-AND-REALTIME-APIs.md): the wire call is GET on the extapi default base, not POST on snat — raw/1606.js is authoritative.",
        "x-evidence": "raw-modules/1606.js:30-47, raw-modules/689.js:63-65",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "description": "Target URL to unblock (\"www.\" prefix stripped by the UI).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "password",
            "in": "query",
            "required": true,
            "description": "Bypass password (UI port message field is \"pw\"; wire field is \"password\").",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profiles",
            "in": "query",
            "required": true,
            "description": "The profiles array (window.profiles) from registration.",
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Bypass grant. Failure answers \"fail\" via the UI port.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BypassResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/nameandemail": {
      "post": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "createAccount",
        "summary": "Create/identify the student account from a scraped identity",
        "description": "Native fetch (not module 149) with form-encoded body built by jQuery.param. Headers: content-type application/x-www-form-urlencoded, authorization (compRand), extensionversion (lowercase). Retried 1 s base, 7 attempts. The identity \"url\" field is one of \"chrome.identity\", \"windows.nativehost\", \"chrome.identity.windows.nativehost\".",
        "x-evidence": "raw-modules/986.js:63-85, raw-modules/986.js:192-223",
        "security": [
          {
            "CompRand": [],
            "ExtensionVersion": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/NameAndEmailRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response fields read: accountId, profiles (kept on window.profiles).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateAccountResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/updatename": {
      "put": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "updateName",
        "summary": "Update the student's scraped display name",
        "x-evidence": "raw-modules/986.js:195-199",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/api/v1/ext/googleprofile": {
      "put": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "updateGoogleProfile",
        "summary": "Update the student's Google profile ID",
        "x-evidence": "raw-modules/986.js:202-223",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "googleProfileId": {
                    "type": "string"
                  }
                },
                "required": [
                  "googleProfileId"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/assets/scripts/ext/submitCoordsGuard.php": {
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "submitGeolocation",
        "summary": "Hourly student geolocation upload (legacy PHP path)",
        "description": "navigator.geolocation.getCurrentPosition with timeout 10 s and enableHighAccuracy; error callback is a no-op. Fired every GEOLOCATION_UPDATE_INTERVAL_MS = 3600000 (1 h), once immediately on enable; gated on admin.monitorEnabled AND a license set containing \"admin\". Fire-and-forget — response never read.",
        "x-evidence": "raw-modules/1556.js:16-51, src/336-intervals.js:21",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/GeolocationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read (fire-and-forget)."
          }
        }
      }
    },
    "/api/v1/ext/user": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "getUser",
        "summary": "Fetch user/account state",
        "description": "Merged verbatim into state.user. Drives the OrgRand mismatch check (if server orgRand is not among the locally installed Chromium/GoGuardian License extension IDs, the extension wipes localStorage and reloads after 60 s) and the email mismatch check (emailOnFile vs chrome.identity email triggers the same wipe).",
        "x-evidence": "raw-modules/1404.js:10-17, raw-modules/1338.js:28-47, src/1521-user.js:110-178",
        "responses": {
          "200": {
            "description": "User payload merged into state.user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ext/privacy": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getPrivacySettings",
        "summary": "Fetch privacy settings (delivers the apiMap and feature gates)",
        "description": "Polled every 1000000 ms (task \"updatePrivacySettings\"); response cached at chrome.storage.local key \"local_data\". The privacy response installs apiMap (full-path -> host overrides for the module-149 client) on every refresh. HTTP 403 wipes localStorage and reloads the extension.",
        "x-evidence": "raw-modules/689.js:101-107, src/1523-privacy.js:64-111",
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "description": "Current privacy version (client-passed cache-buster).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Privacy settings bundle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivacyResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/policy": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getAdminPolicy",
        "summary": "Fetch admin blocking policy (site filters)",
        "description": "Polled every 900000 ms (15 min, GETPOLICY_INTERVAL_MS).",
        "x-evidence": "raw-modules/852.js:99-105, src/336-intervals.js:22",
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "timezone",
            "in": "query",
            "required": false,
            "description": "moment.tz.guess() result.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Admin policy (site filters, categories, schedules). Shape not exhaustively reconstructed — schema is loose.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PolicyResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/experiments": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getExperiments",
        "summary": "Fetch experiment assignment lists",
        "description": "Validated response; retried 1 s base, 10 attempts.",
        "x-evidence": "raw-modules/1756.js:10-22",
        "responses": {
          "200": {
            "description": "Experiment assignment/exposure lists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperimentsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/notifications": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getNotifications",
        "summary": "Poll for student-visible notifications",
        "description": "Polled every 900000 ms (GET_NOTIFICATIONS_INTERVAL_MS).",
        "x-evidence": "raw-modules/1772.js:10-14, src/336-intervals.js:33",
        "responses": {
          "200": {
            "description": "Notification list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/chat-messages": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Classroom & Calls"
        ],
        "operationId": "getChatMessages",
        "summary": "Fetch classroom chat history for a session",
        "description": "Client parseInts each message's studentId from the response.",
        "x-evidence": "raw-modules/1709.js:20-33",
        "parameters": [
          {
            "name": "sessionId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Chat history.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChatMessagesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/ext/diagnose": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Utilities"
        ],
        "operationId": "submitDiagnostics",
        "summary": "Dump full extension state on checkup navigation",
        "description": "Fired when a tab navigates to https://checkup.goguardian.com. Body is a JSON.stringify of the full redux state plus Pusher channel info.",
        "x-evidence": "raw-modules/1530.js:34, raw-modules/1530.js:82-88",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DiagnoseRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/api/v1/ext/deviceId": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Registration & Identity"
        ],
        "operationId": "registerDeviceId",
        "summary": "Register the enterprise-directory device ID",
        "description": "dataType \"text\"; retried 1 s base, 3 attempts. Sends deviceId, or a reason (\"Not Provisioned\") when the enterprise API did not supply one.",
        "x-evidence": "raw-modules/1607.js:77-92",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/DeviceIdRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read (dataType text)."
          }
        }
      }
    },
    "/api/v1/ext/app-exts": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "getAppExtsSnapshot",
        "summary": "Fetch the server's snapshot of installed extensions/apps",
        "description": "Used to diff against the local chrome.management list before syncing.",
        "x-evidence": "raw-modules/1569.js:92-98",
        "responses": {
          "200": {
            "description": "Installed id list; shape not reconstructed (raw 1569 diff logic)."
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/settings": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getSmartFilteringSettings",
        "summary": "Fetch smart-filtering configuration (hosted domains, proxy matchers)",
        "description": "Response cached; used to configure proxy/direct-IP blocking.",
        "x-evidence": "raw-modules/1641.js:92-98",
        "responses": {
          "200": {
            "description": "Smart-filtering settings; shape not reconstructed."
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/explicit/{version}/model.json": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getExplicitImageModel",
        "summary": "Fetch the TF.js explicit-image model for a version",
        "x-evidence": "raw-modules/1625.js:180",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "TF.js model JSON. Consumed by the explicit-image-filter pipeline.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/explicit/{version}/metadata.json": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getExplicitImageModelMetadata",
        "summary": "Fetch metadata for the explicit-image model version",
        "x-evidence": "raw-modules/1625.js:243",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Model metadata JSON.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/proxies/version/current": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getProxiesModelCurrentVersion",
        "summary": "Current keyword-model version for proxy detection",
        "x-evidence": "raw-modules/1644.js:9",
        "responses": {
          "200": {
            "description": "Version descriptor consumed by module 1644."
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/proxies/{version}/model.json": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getProxiesModel",
        "summary": "Fetch proxy keyword model for a version",
        "description": "Synced every 900000 ms.",
        "x-evidence": "raw-modules/1644.js:26",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Keyword model JSON."
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/threats/version/current": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getThreatsModelCurrentVersion",
        "summary": "Current keyword-model version for threats detection",
        "x-evidence": "raw-modules/1646.js:9",
        "responses": {
          "200": {
            "description": "Version descriptor consumed by module 1646."
          }
        }
      }
    },
    "/api/v1/ext/smart-filtering/models/threats/{version}/model.json": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Settings & Policy"
        ],
        "operationId": "getThreatsModel",
        "summary": "Fetch threats keyword model for a version",
        "x-evidence": "raw-modules/1646.js:27",
        "parameters": [
          {
            "name": "version",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Keyword model JSON."
          }
        }
      }
    },
    "/api/v2/ext/lesson-plan/{id}": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Classroom & Calls"
        ],
        "operationId": "getLessonPlan",
        "summary": "Fetch a lesson plan scene definition",
        "description": "Hand-rolled client with lowercase authorization/extensionversion headers; backoff initial 1 s, max 10 s with jitter; retried only on network errors and 5xx.",
        "x-evidence": "raw-modules/1047.js:83-130",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lesson-plan scene JSON (restricted sites etc.); shape not reconstructed.",
            "content": {
              "application/json": {}
            }
          }
        }
      }
    },
    "/api/v2/ext/ggt-calls/{callId}/participants": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Classroom & Calls"
        ],
        "operationId": "getCallParticipants",
        "summary": "Fetch participants of a teacher-student video call",
        "description": "contentType application/json; retried 500 ms base, 5 attempts.",
        "x-evidence": "raw-modules/1051.js:196-218",
        "parameters": [
          {
            "name": "callId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Call participant list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParticipantsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/ext/ggt-calls/{callId}/participant/{id}": {
      "servers": [
        {
          "url": "https://snat.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Classroom & Calls"
        ],
        "operationId": "getCallParticipant",
        "summary": "Fetch a single call participant",
        "x-evidence": "raw-modules/1051.js:196-218",
        "parameters": [
          {
            "name": "callId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One participant record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Participant"
                }
              }
            }
          }
        }
      }
    },
    "/v1/entitybatchpreauth": {
      "servers": [
        {
          "url": "https://quiddity.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "submitEntityBatch",
        "summary": "Main surveillance pipeline — gzipped Avro entity batches",
        "description": "Headers set in beforeSend: Authorization (compRand || \"Unknown\"), ExtensionVersion (|| \"Unknown\"), Content-type application/binary, Content-encoding gzip. Body is a Blob([binary], type application/binary) where binary = pako.gzip(1-byte schema version + Avro-serialized EntityBatchPreAuth). Schema version = 13 (shipped as avro/resources/EntityBatchPreAuth.version). Flushed by TaskManager task \"entities_flush\" every 60000 ms (1000 ms fast interval during mlPipeline rate-increase); immediate flush on a new search entity when mlPipeline is enabled; final flush when monitoring disables. Gated on admin.monitorEnabled (or the dataCollectionAllEntities feature). On POST failure the pending keys are NOT marked flushed (only statsd admin.entity_submission.error) so they retry next flush; entities that fail Avro trial serialization are dropped and the rest ships. Internal pipeline name \"kinesis\".",
        "x-evidence": "src/1741-flush-entities-thunk.js:83-139, src/1740-entities-flush.js:44-110, src/1736-entity-manager-v3.js, source/background/…/EntityBatchPreAuth.avsc",
        "requestBody": {
          "required": true,
          "content": {
            "application/binary": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "gzip(0x0D || avro(EntityBatchPreAuth)). The decoded record shape is documented as the informational components EntityBatchPreAuthRecord / EntityPreAuth."
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read; success releases the batch keys."
          }
        }
      }
    },
    "/v1/roll-call": {
      "servers": [
        {
          "url": "https://waluigi.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "rollCall",
        "summary": "Protobuf attendance heartbeat",
        "description": "Body: protobuf waluigi.RollCallRequest.create({...}).encode().finish() — field 1 extensionVersion (string, tag 0x0A), field 2 ip (uint64, tag 0x10; ipv4 packed as 256*(256*(256*a+b)+c)+d). Content-Type application/octet-stream. TaskManager task \"rollCall\" every 300000 ms (5 min) plus immediate fire on monitor enable. Gated on priv.settings.features.eventTransportSettings.roll_call existing and !rollCall.disabled AND admin.monitorEnabled. Retried 1 s base, 3 attempts; failure statsd \"luigi_roll_call.error\". Client checks only response.ok; RPC return type waluigi.SuccessResponse.",
        "x-evidence": "src/1541-attendance.js:48-88, raw-modules/989.js:64-101",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "protobuf RollCallRequest { extensionVersion: string (f1), ip: uint64 (f2) }"
            }
          }
        },
        "responses": {
          "200": {
            "description": "waluigi.SuccessResponse; only response.ok checked."
          }
        }
      }
    },
    "/v1/create-apps-exts": {
      "servers": [
        {
          "url": "https://waluigi.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "createAppsExts",
        "summary": "Sync newly installed apps/extensions (protobuf)",
        "description": "protobuf CreateAppsExtsRequest: f1 extensionVersion (string), f2 ip (uint64), f3 repeated AppsExtsPayload { extID f1, extName f2, extJSON f3, isApp f4, description f5, homepageURL f6, iconURL f7 }. Content-Type application/octet-stream, processData false. Diffed against GET /api/v1/ext/app-exts before sending.",
        "x-evidence": "raw-modules/1569.js:150-162, raw-modules/989.js:270-298",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "protobuf CreateAppsExtsRequest"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v1/delete-apps-exts": {
      "servers": [
        {
          "url": "https://waluigi.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "deleteAppsExts",
        "summary": "Sync removed apps/extensions (protobuf)",
        "description": "protobuf DeleteAppsExtsRequest: f1 extensionVersion (string), f2 ip (uint64), f3 repeated ids (string). Sent at most every SEND_EXTENSIONS_RATE_MS = 3600000 ms (1 h).",
        "x-evidence": "raw-modules/1569.js:169-186, raw-modules/989.js:634-660, src/336-intervals.js:24",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "protobuf DeleteAppsExtsRequest"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v1/snapshots": {
      "servers": [
        {
          "url": "https://screenshots.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "uploadSnapshot",
        "summary": "Upload a live teacher-view screenshot (raw JPEG)",
        "description": "opts { processData: false, contentType: \"image/jpeg\" }; body is the raw JPEG bytes after canvas resize to maxDimension (800 legacy; liveStateVersion >= 4: 500, or 1600 during resolution-increase) at quality 80. The response must echo the client md5 as hash or the client throws \"hash mismatch!\"; url is the teacher-facing link. md5 dedupe: a NodeCache (stdTTL 30 s) keyed by md5 skips re-upload; expirations are debounced (500 ms) into a \"snapshotsExpired\" event.",
        "x-evidence": "src/1716-image-handler.js:82-228, src/1715-live-state.js:199-201",
        "requestBody": {
          "required": true,
          "content": {
            "image/jpeg": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "Raw JPEG bytes (md5 computed over these)."
            }
          }
        },
        "responses": {
          "200": {
            "description": "Snapshot descriptor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SnapshotResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/snapshots/{hash}": {
      "servers": [
        {
          "url": "https://screenshots.goguardian.com"
        }
      ],
      "put": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "keepSnapshotAlive",
        "summary": "Snapshot TTL keepalive",
        "description": "TaskManager task \"image-handler-update-ttl\" every 20000 ms (20 s); skipped when no snapshot is cached. Body is the literal string \"{}\".",
        "x-evidence": "src/1716-image-handler.js:96-100, src/1716-image-handler.js:252-258",
        "parameters": [
          {
            "name": "hash",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v1/activity-snapshots": {
      "servers": [
        {
          "url": "https://screenshots.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "uploadActivitySnapshot",
        "summary": "Upload a permanent activity screenshot (data URL)",
        "description": "JPEG data-URL captured at quality 75 (snapHeavy(null, 75)); cached with TTL 960 s and shipped when its screenshotTime falls inside the entity flush window or its seqId is in a server-requested backfill (seqIdsToCollect).",
        "x-evidence": "src/411-snapper.js:69-81, src/1736-entity-manager-v3.js:186-243",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivitySnapshotRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v1/statsd": {
      "servers": [
        {
          "url": "https://countvoncount.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Telemetry"
        ],
        "operationId": "submitStatsdBatch",
        "summary": "StatsD metrics batch (1-byte version + Avro, application/binary)",
        "description": "Headers: Authorization (compRand || \"Unknown\"), ExtensionVersion, Content-type application/binary. Blob([versionByte ++ avro.toBuffer(record)]) where versionByte = StatsDBatch.version = 1 (avro/resources/StatsDBatch.version). Flushed by task \"statsd_flush\" every 30000 ms; buffer capped at 100 metrics (keeps newest); retried 1 s base, 3 attempts, failure re-queues with statsd \"statsd.submit_error\". Metric names are prefixed \"chromium_m.\".",
        "x-evidence": "src/182-statsd-client.js:42-115",
        "requestBody": {
          "required": true,
          "content": {
            "application/binary": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "0x01 || avro(StatsDBatchRecord)"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v2/logs": {
      "servers": [
        {
          "url": "https://countvoncount.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Telemetry"
        ],
        "operationId": "submitLogs",
        "summary": "Log shipping (JSON batch; server-side Avro schema)",
        "description": "contentType application/json, processData false; body JSON.stringify( { logs, submissionTime }). Flushed by task \"logs_flush\" every 30000 ms, 100 records per batch; retried 1 s base, 3 attempts, failure re-queues with statsd \"logs.submit_error\". Client records carry severity / log / stack_trace (module-678 logger); every record is pre-tagged with account_id, extension_version, os, org_id, sub_account_id. Server deserializes with avro LogsBatch.Log { logMessage, level INFO|WARN|ERROR, timestampMs, errorName, errorMessage, errorStack } — the exact client->server key mapping is not visible in the extension (uncertainty N3 of the extraction).",
        "x-evidence": "src/24-log-client.js:44-180",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LogsRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v2/predictions": {
      "servers": [
        {
          "url": "https://x3-predictor.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "predictX3V2",
        "summary": "Keyword-list self-harm prediction (gzipped base64)",
        "description": "contentType: false, timeout 10000 ms, headers Authorization + X-GG-X3EXTVER. Body: btoa(pako.gzip(JSON.stringify({words}))) — a base64 string of gzipped JSON with a single array field \"words\". Retried 3 attempts at 500 * 2^n ms. Response data.prediction.{category, probability}.",
        "x-evidence": "raw-modules/1610.js:26-46",
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              },
              "description": "btoa(gzip(JSON({words:[...]})))"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "prediction": {
                      "$ref": "#/components/schemas/X3Prediction"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/predictions": {
      "servers": [
        {
          "url": "https://x3-predictor.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "predictX3V3",
        "summary": "Full-text self-harm prediction (gzip binary)",
        "description": "Headers Authorization + ExtensionVersion; body pako.gzip(JSON.stringify({fullText})). This endpoint is used ONLY when the X3 policy's disabledCategories includes \"self-harm\" (which disables it elsewhere). Response data.prediction.{category, probability, threshold{low,medium,high}}.",
        "x-evidence": "raw-modules/1009.js:56-77",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              },
              "description": "gzip(JSON({fullText}))"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Prediction with thresholds.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "prediction": {
                      "type": "object",
                      "properties": {
                        "category": {
                          "type": "string"
                        },
                        "probability": {
                          "type": "number"
                        },
                        "threshold": {
                          "type": "object",
                          "properties": {
                            "low": {
                              "type": "number"
                            },
                            "medium": {
                              "type": "number"
                            },
                            "high": {
                              "type": "number"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/proxy/v1/predictions": {
      "servers": [
        {
          "url": "https://x3-predictor.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "predictX3Proxy",
        "summary": "Full-text prediction via CORS proxy (base64 deflate)",
        "description": "contentType: false. Body: btoa(pako.deflate(JSON.stringify({ fullText (capped at 30000 chars, truncated at the last space), requestID (ISO date with ms truncated to 19 chars, \"T\" replaced by \"-\", then \"-\" + url.slice(0,500)) }))). Response data.{probability, threshold{very_high, high, medium}}.",
        "x-evidence": "raw-modules/1009.js:32-92, raw-modules/1643.js:144-167",
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              },
              "description": "btoa(deflate(JSON({fullText, requestID})))"
            }
          }
        },
        "responses": {
          "200": {
            "description": "Proxy prediction result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "probability": {
                      "type": "number"
                    },
                    "threshold": {
                      "type": "object",
                      "properties": {
                        "very_high": {
                          "type": "number"
                        },
                        "high": {
                          "type": "number"
                        },
                        "medium": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reports": {
      "servers": [
        {
          "url": "https://x3-reporter.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "createX3Report",
        "summary": "Submit a self-harm alert report",
        "description": "contentType \"application/json; charset=utf-8\", timeout 7000 ms, headers Authorization + X-GG-X3EXTVER. Retried 3 attempts at 500 * 2^n ms. Response {reportId, contentDescriptor, screenshotDescriptor, imageDescriptor}.",
        "x-evidence": "raw-modules/697.js:31-47",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/X3ReportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Report created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/X3ReportResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/beacon/reports": {
      "servers": [
        {
          "url": "https://x3-reporter.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Beacon (crisis response)"
        ],
        "operationId": "createBeaconReport",
        "summary": "Submit a Beacon crisis report",
        "description": "Hand-rolled fetch: lowercase headers authorization / x-gg-x3extver. JSON {url, timestamp, prediction, closedMessage: false, didMessage, seqId, model: \"FULLPAGE_PREDICTOR\" | \"TV_SEARCH_PREDICTOR\"}. Retried 1 s base, 3 attempts. Response {reportId, contentDescriptor, screenshotDescriptor}.",
        "x-evidence": "src/1652-beacon-reporter.js:538-562",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BeaconReportRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Beacon report created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BeaconReportResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/beacon/reports/{reportId}": {
      "servers": [
        {
          "url": "https://x3-reporter.goguardian.com"
        }
      ],
      "put": {
        "tags": [
          "Beacon (crisis response)"
        ],
        "operationId": "closeBeaconReport",
        "summary": "Mark a Beacon report's crisis message closed",
        "description": "Lowercase headers. Body {closedMessage: true}. Retried 1 s base, 3 attempts. Fired after the crisis help modal is dismissed.",
        "x-evidence": "src/1652-beacon-reporter.js:436-459",
        "parameters": [
          {
            "name": "reportId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "closedMessage"
                ],
                "properties": {
                  "closedMessage": {
                    "type": "boolean",
                    "enum": [
                      true
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/v1/policy": {
      "servers": [
        {
          "url": "https://x3-policy-maker.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "getX3Policy",
        "summary": "Smart Alerts trigger policy",
        "description": "timeout 7000 ms, retried 1 s base, 10 attempts. Response cached under key \"triggers\". Response fields read: triggers.messages[] / triggers.blocks[] (items: triggerId, name, message, threshold, category, ou, triggerSources, blockTarget), blockedCategories (mapped to disabledCategories), disabledImageFilteringSiteCategories.",
        "x-evidence": "raw-modules/1621.js:100-153",
        "responses": {
          "200": {
            "description": "X3 trigger policy.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/X3Policy"
                }
              }
            }
          }
        }
      }
    },
    "/v1/beacon/policy": {
      "servers": [
        {
          "url": "https://x3-policy-maker.goguardian.com"
        }
      ],
      "get": {
        "operationId": "getBeaconPolicy",
        "tags": [
          "Beacon (crisis response)"
        ],
        "summary": "Beacon deployment policy (15-minute refresh)",
        "description": "Refreshed every 15 min, cached under key \"beacon\". HTTP 400 with error code 9 means \"not deployed\" and silently disables Beacon.",
        "x-evidence": "raw-modules/1651.js:92-98",
        "responses": {
          "200": {
            "description": "Beacon policy (may be an empty deployment marker).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BeaconPolicy"
                }
              }
            }
          },
          "400": {
            "description": "Error code 9 = Beacon not deployed for this org."
          }
        }
      }
    },
    "/v1/beacon/fullpage": {
      "servers": [
        {
          "url": "https://beacon-fullpage-predictor.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Beacon (crisis response)"
        ],
        "operationId": "predictBeaconFullpage",
        "summary": "Full-page crisis scan",
        "description": "Hand-rolled fetch with lowercase headers. Body is the content object produced by the Beacon miner with its url field set. Retried 1 s base, 3 attempts. Response data.prediction.{phase, probability}.",
        "x-evidence": "src/1652-beacon-reporter.js:203, src/1652-beacon-reporter.js:460-484",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Miner content object with url set."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fullpage prediction.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "prediction": {
                      "$ref": "#/components/schemas/BeaconPrediction"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/threats-and-violence/predict": {
      "servers": [
        {
          "url": "https://ml-endpoint.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Beacon (crisis response)"
        ],
        "operationId": "predictThreatsAndViolence",
        "summary": "Threats-and-violence search-term prediction",
        "description": "JSON {searchterm}. Response data.{phase, probability}. Used by the TV_SEARCH_PREDICTOR Beacon model on search terms.",
        "x-evidence": "src/1652-beacon-reporter.js:660-684",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "searchterm"
                ],
                "properties": {
                  "searchterm": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "TV prediction.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "phase": {
                      "type": "string"
                    },
                    "probability": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/device-notifications/v1/get-notifications": {
      "servers": [
        {
          "url": "https://x3-policy-maker.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "getDeviceNotifications",
        "summary": "Poll admin device notifications",
        "description": "Retried 1 s base, 10 attempts. Response read for notifications destined to this device/session (processed through the student-notifications module).",
        "x-evidence": "raw-modules/1043.js",
        "responses": {
          "200": {
            "description": "Pending notifications.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Exact shape not reconstructed."
                }
              }
            }
          }
        }
      }
    },
    "/device-notifications/v1/ack": {
      "servers": [
        {
          "url": "https://x3-policy-maker.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Smart Alerts (X3)"
        ],
        "operationId": "ackDeviceNotifications",
        "summary": "Acknowledge device notifications",
        "description": "JSON {ack_ids:[...]} of notification ids being acknowledged. Retried 1 s base, 10 attempts.",
        "x-evidence": "raw-modules/1043.js",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ack_ids"
                ],
                "properties": {
                  "ack_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/api/v1/ext/fullurl/categories": {
      "servers": [
        {
          "url": "https://panther.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Utilities"
        ],
        "operationId": "categorizeUrl",
        "summary": "Server-side URL categorization",
        "description": "Query parameter url (the full URL to categorize). Retried 1 s base, 3 attempts. Response read as {categories:[{cats:[...]}]} (first categories entry used).",
        "x-evidence": "raw-modules/859.js:12-30",
        "parameters": [
          {
            "name": "url",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Category lookup result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "cats": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/taskiness_score": {
      "servers": [
        {
          "url": "https://taskiness-preprocessor.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Taskiness & Video"
        ],
        "operationId": "scoreTaskiness",
        "summary": "On-task scoring for a subject's full text",
        "description": "URL carries a subject query parameter (e.g. ?subject=<subject>). Body is the plain-text fullText (content type text/plain). Response is a map subject -> {score} plus a timestamp.",
        "x-evidence": "raw-modules/1725.js:267-299",
        "parameters": [
          {
            "name": "subject",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              },
              "description": "The full text to score."
            }
          }
        },
        "responses": {
          "200": {
            "description": "Taskiness scores keyed by subject.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object",
                    "properties": {
                      "score": {
                        "type": "number"
                      }
                    }
                  },
                  "description": "Plus a timestamp field alongside the subject keys."
                }
              }
            }
          }
        }
      }
    },
    "/v1/video": {
      "servers": [
        {
          "url": "https://play-button.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Taskiness & Video"
        ],
        "operationId": "getVideoMetadata",
        "summary": "YouTube video metadata lookup",
        "description": "Query parameter videoID. Response data.video is read (fields consumed: ID, ageRestricted, channelID, categoryID, plus others remapped into VideoMetadata).",
        "x-evidence": "raw-modules/1602.js:15-38",
        "parameters": [
          {
            "name": "videoID",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Video metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "video": {
                      "type": "object",
                      "description": "Fields actually consumed: ID, ageRestricted, channelID, categoryID (remapped into VideoMetadata).",
                      "properties": {
                        "ID": {
                          "type": "string"
                        },
                        "ageRestricted": {
                          "type": "boolean"
                        },
                        "channelID": {
                          "type": "string"
                        },
                        "categoryID": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/ip": {
      "servers": [
        {
          "url": "https://ip.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Utilities"
        ],
        "operationId": "getPublicIp",
        "summary": "Public IPv4 discovery (no auth)",
        "description": "security: [] — no compRand/ExtensionVersion headers are sent. Response is a dotted quad string. Refresh task every 60000 ms; timeout 5 s, retried 1 s base, 5 attempts; fallback \"0.0.0.0\".",
        "x-evidence": "raw-modules/692.js:41-77",
        "security": [],
        "responses": {
          "200": {
            "description": "Dotted-quad IPv4 string.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "example": "203.0.113.7"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/ext": {
      "servers": [
        {
          "url": "https://sakura.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Pusher Auth"
        ],
        "operationId": "pusherAuth",
        "summary": "Pusher channel authentication",
        "description": "Header Authorization (compRand). Query parameters: version, protocolVersion=1, capabilities (CSV of numeric capability bits — base [1,17,18,19,21] plus 2,20 on non-Windows and 3,4 on Windows), clientType=extension, os, liveStateVersion=6, plus the pusher channel_name and socket_id. Response {auth, channel_data} is ASSUMED — it is consumed inside the vendored pusher-js library, not visible to the extension code (uncertainty N1).",
        "x-evidence": "src/475-pusher.js:98-165, src/987-auth-endpoint.js:113-121, src/1642-live-state.js:364-433",
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "protocolVersion",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "enum": [
                1
              ]
            }
          },
          {
            "name": "capabilities",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "CSV of numeric capability bits (1..22)."
          },
          {
            "name": "clientType",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "extension"
              ]
            }
          },
          {
            "name": "os",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "liveStateVersion",
            "in": "query",
            "required": true,
            "schema": {
              "type": "integer",
              "enum": [
                6
              ]
            }
          },
          {
            "name": "channel_name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "socket_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Pusher auth payload (shape assumed, consumed by vendored pusher-js).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "auth": {
                      "type": "string"
                    },
                    "channel_data": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/ext/sample": {
      "servers": [
        {
          "url": "https://tivan.goguardian.com"
        }
      ],
      "post": {
        "tags": [
          "Surveillance"
        ],
        "operationId": "uploadSample",
        "summary": "Evidence sample bundle upload (screenshot + JSON artifacts)",
        "description": "JSON {snapshotIds:[...], data:[{filename (\"screenshot.jpeg\" | \"content.json\" | \"prediction.json\" | \"metadata.json\"), dataURI}]}. On failure the sampler enters a 15-minute cooldown before retrying.",
        "x-evidence": "raw-modules/866.js:41-44",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SampleRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      }
    },
    "/": {
      "description": "Shared root path: three distinct hosts serve \"/\" — the pre-signed S3 evidence bucket (POST), www.google.com (HEAD, clock sync), and blocked.goguardian.com (GET, generic block page). Each operation carries its own servers override, which OpenAPI 3.1 permits at the operation level.",
      "post": {
        "servers": [
          {
            "url": "https://{bucket}.s3.amazonaws.com",
            "variables": {
              "bucket": {
                "description": "Evidence upload bucket from the sample descriptor."
              }
            }
          }
        ],
        "tags": [
          "S3 Upload"
        ],
        "operationId": "s3MultipartUpload",
        "summary": "S3 multipart evidence upload (pre-signed, no auth headers)",
        "description": "security: [] — the auth is baked into the pre-signed form fields from the sample descriptor (descriptor.auth): Content-Type, key, policy, x-amz-algorithm, x-amz-credential, x-amz-date, x-amz-signature, plus the file field carrying the blob.",
        "x-evidence": "raw-modules/1011.js:9-34",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "key",
                  "policy",
                  "x-amz-algorithm",
                  "x-amz-credential",
                  "x-amz-date",
                  "x-amz-signature",
                  "file"
                ],
                "properties": {
                  "Content-Type": {
                    "type": "string"
                  },
                  "key": {
                    "type": "string"
                  },
                  "policy": {
                    "type": "string"
                  },
                  "x-amz-algorithm": {
                    "type": "string"
                  },
                  "x-amz-credential": {
                    "type": "string"
                  },
                  "x-amz-date": {
                    "type": "string"
                  },
                  "x-amz-signature": {
                    "type": "string"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "The evidence blob (screenshot or JSON artifact)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Response not read."
          }
        }
      },
      "head": {
        "servers": [
          {
            "url": "https://www.google.com"
          }
        ],
        "tags": [
          "Utilities"
        ],
        "operationId": "syncClockFromGoogle",
        "summary": "Clock sync via Google Date header (no auth)",
        "description": "security: [] — HEAD https://www.google.com/ with timeout 5 s. Reads only the Date response header to correct the extension clock; on failure the system clock is trusted. Response body never consumed.",
        "x-evidence": "raw-modules/693.js:78-105",
        "security": [],
        "responses": {
          "200": {
            "description": "Only the Date header is read; body ignored.",
            "headers": {
              "Date": {
                "schema": {
                  "type": "string"
                },
                "description": "Server time used for clock sync."
              }
            }
          }
        }
      },
      "get": {
        "servers": [
          {
            "url": "https://blocked.goguardian.com"
          },
          {
            "url": "https://staging.blocked.goguardian.com"
          }
        ],
        "tags": [
          "Block Pages"
        ],
        "operationId": "showGenericBlockPage",
        "summary": "Generic admin/teacher/parent block page (no auth)",
        "description": "security: [] — opened as a tab (not XHR), so no auth headers. Path chosen by block reason: ADMIN_* / BLOCK_* / UNKNOWN reasons land here. Query ctx = base64(sorted URLSearchParams) with oi (orgID multiplied by 1234567), ou, st (sender tab type chromium-m|cip|na|shield|unknown), rs (reason enum string), sci, api, afi, pfi, x3rpi, tsi, tsfi, tsans (repeatable), v=1; required oi/ou/st/rs. Query sum = crc32(ctx) hex, padStart 8. The page validates by crc32 match, v < 8192, and oi positive and divisible by 1234567. Staging host is used when the extension build targets staging.",
        "x-evidence": "src/409-block-url.js:66-125, src/409-block-url.js:139-231, src/409-block-url.js:283-305",
        "security": [],
        "parameters": [
          {
            "name": "ctx",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "base64 of the sorted, stringified URLSearchParams blob."
          },
          {
            "name": "sum",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "crc32(ctx) as zero-padded 8-char hex."
          }
        ],
        "responses": {
          "200": {
            "description": "HTML block page (navigated to, not parsed as API).",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/ping": {
      "servers": [
        {
          "url": "https://extension.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Utilities"
        ],
        "operationId": "nativeAgentPing",
        "summary": "Native-agent watchdog ping (no auth)",
        "description": "security: [] — no auth headers. Polled every 5 min while the native agent is healthy; expects HTTP 200 with body \"pong\". On failure the poll tightens to 10 s; 6 consecutive misses mark the native agent uninstalled, a body change triggers a reload of the agent, and the loop never runs on ChromeOS devices.",
        "x-evidence": "src/1517-native-agent-watcher.js:47-100",
        "security": [],
        "responses": {
          "200": {
            "description": "Literally the string \"pong\".",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "pong"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/AccountChooser": {
      "servers": [
        {
          "url": "https://accounts.google.com"
        }
      ],
      "get": {
        "tags": [
          "Utilities"
        ],
        "operationId": "scrapeGoogleAccountChooser",
        "summary": "Google account name scrape (no auth, response not parsed)",
        "description": "security: [] — fetches the Google account chooser page to read the signed-in Google account name from the DOM in a separate context. The HTTP response is not consumed as an API. A sibling scrape GET https://myaccount.google.com/ (also security: [], response not consumed) reads the account name the same way; it shares this operation's path key spelling \"/\" with three other hosts, so it cannot carry its own path item in this document.",
        "x-evidence": "raw-modules/986.js:120",
        "security": [],
        "responses": {
          "200": {
            "description": "HTML page; response not consumed as API data."
          }
        }
      }
    },
    "/parent/block.html": {
      "servers": [
        {
          "url": "https://blocked.goguardian.com"
        },
        {
          "url": "https://staging.blocked.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Block Pages"
        ],
        "operationId": "showParentBlockPage",
        "summary": "Parent site-filter block page (no auth)",
        "description": "security: [] — tab navigation, no auth headers. Reason mapping: PARENT_SITE_FILTER lands here. Same ctx/sum query contract as the generic block page.",
        "x-evidence": "src/409-block-url.js:66-125, src/409-block-url.js:139-231",
        "security": [],
        "parameters": [
          {
            "name": "ctx",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "base64(sorted URLSearchParams): oi (orgID*1234567), ou, st, rs=PARENT_SITE_FILTER, v=1, optional sci/api/afi/pfi."
          },
          {
            "name": "sum",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "crc32(ctx) hex padStart 8."
          }
        ],
        "responses": {
          "200": {
            "description": "HTML block page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/parent/pause.html": {
      "servers": [
        {
          "url": "https://blocked.goguardian.com"
        },
        {
          "url": "https://staging.blocked.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Block Pages"
        ],
        "operationId": "showParentPausePage",
        "summary": "Parent pause / scheduled-pause page (no auth)",
        "description": "security: [] — tab navigation. Reason mapping: PARENT_PAUSE and PARENT_SCHEDULED_PAUSE land here. Same ctx/sum contract.",
        "x-evidence": "src/409-block-url.js:66-125, src/409-block-url.js:139-231",
        "security": [],
        "parameters": [
          {
            "name": "ctx",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "base64(sorted URLSearchParams): oi, ou, st, rs=PARENT_PAUSE|PARENT_SCHEDULED_PAUSE, v=1."
          },
          {
            "name": "sum",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "crc32(ctx) hex padStart 8."
          }
        ],
        "responses": {
          "200": {
            "description": "HTML pause page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/teacher/block.html": {
      "servers": [
        {
          "url": "https://blocked.goguardian.com"
        },
        {
          "url": "https://staging.blocked.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Block Pages"
        ],
        "operationId": "showTeacherBlockPage",
        "summary": "Teacher scene block page (no auth)",
        "description": "security: [] — tab navigation. Reason mapping: TEACHER_SCENE lands here. Same ctx/sum contract, with tsi (teacherSceneId), tsfi (teacherSceneFilterId), and repeatable tsans in ctx.",
        "x-evidence": "src/409-block-url.js:66-125, src/409-block-url.js:139-231",
        "security": [],
        "parameters": [
          {
            "name": "ctx",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "base64(sorted URLSearchParams): oi, ou, st, rs=TEACHER_SCENE, tsi, tsfi, tsans (repeatable), v=1."
          },
          {
            "name": "sum",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "crc32(ctx) hex padStart 8."
          }
        ],
        "responses": {
          "200": {
            "description": "HTML block page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/x3/block.html": {
      "servers": [
        {
          "url": "https://blocked.goguardian.com"
        },
        {
          "url": "https://staging.blocked.goguardian.com"
        }
      ],
      "get": {
        "tags": [
          "Block Pages"
        ],
        "operationId": "showX3BlockPage",
        "summary": "Smart Alerts (X3) block page (no auth)",
        "description": "security: [] — tab navigation. Reason mapping: X3_REPORT lands here. ctx additionally carries x3rpi (x3ReportPublicId).",
        "x-evidence": "src/409-block-url.js:66-125, src/409-block-url.js:139-231",
        "security": [],
        "parameters": [
          {
            "name": "ctx",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "base64(sorted URLSearchParams): oi, ou, st, rs=X3_REPORT, x3rpi, v=1."
          },
          {
            "name": "sum",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "crc32(ctx) hex padStart 8."
          }
        ],
        "responses": {
          "200": {
            "description": "HTML block page with crisis resources.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "CompRand": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "Raw compRand token — NO \"Bearer\" prefix. Issued by POST /api/v1/ext/register, persisted at localStorage.compRand. Some hand-rolled fetches send it as the lowercase header \"authorization\"."
      },
      "ExtensionVersion": {
        "type": "apiKey",
        "in": "header",
        "name": "ExtensionVersion",
        "description": "The extension's own version string. Hand-rolled fetches may send it lowercase (\"extensionversion\"); the X3 predictor pair uses X-GG-X3EXTVER instead."
      }
    },
    "schemas": {
      "RegisterRequest": {
        "type": "object",
        "required": [
          "orgRands"
        ],
        "properties": {
          "orgRands": {
            "type": "string"
          }
        },
        "description": "jQuery form body (application/x-www-form-urlencoded)."
      },
      "RegisterResponse": {
        "type": "object",
        "required": [
          "compRand"
        ],
        "properties": {
          "compRand": {
            "type": "string",
            "description": "Session token stored at localStorage.compRand."
          }
        }
      },
      "VersionRequest": {
        "type": "object",
        "required": [
          "extVer"
        ],
        "properties": {
          "extVer": {
            "type": "string"
          }
        }
      },
      "BypassResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "time": {
            "type": "number",
            "description": "Bypass window end (epoch ms)."
          }
        }
      },
      "NameAndEmailRequest": {
        "type": "object",
        "required": [
          "name",
          "email"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "CreateAccountResponse": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string"
          },
          "success": {
            "type": "boolean"
          }
        }
      },
      "GeolocationRequest": {
        "type": "object",
        "required": [
          "latitude",
          "longitude"
        ],
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          }
        },
        "description": "submitCoordsGuard.php form body; fire-and-forget."
      },
      "UserResponse": {
        "type": "object",
        "required": [
          "orgRand",
          "email"
        ],
        "properties": {
          "orgRand": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "orgId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        },
        "description": "Mismatch of orgRand/email against cached values wipes local state."
      },
      "NameAndEmailSelectors": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "PrivacyResponse": {
        "type": "object",
        "properties": {
          "apiMap": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional full-path -> host override map, installed with privacy settings; keyed by the complete path string (e.g. \"/api/v1/ext/policy\": \"https://alt.goguardian.com\")."
          }
        }
      },
      "PolicyResponse": {
        "type": "object",
        "description": "Deliberately loose: the policy object is merged into settings wholesale. Known top-level groups include scene settings, YouTube category filters, smart-filtering config, off-task alerts, and feature flags."
      },
      "ExperimentsResponse": {
        "type": "object",
        "properties": {
          "experiments": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "NotificationsResponse": {
        "type": "object",
        "properties": {
          "notifications": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "ChatMessagesResponse": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "DiagnoseRequest": {
        "type": "object",
        "required": [
          "data"
        ],
        "properties": {
          "data": {
            "type": "string",
            "description": "JSON.stringify of a diagnostic state snapshot."
          }
        }
      },
      "DeviceIdRequest": {
        "type": "object",
        "properties": {
          "deviceId": {
            "type": "string"
          }
        },
        "description": "Plain-text response (dataType text) with the resolved device id."
      },
      "ParticipantsResponse": {
        "type": "object",
        "properties": {
          "participants": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Participant"
            }
          }
        }
      },
      "Participant": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "joinedAt": {
            "type": "number"
          }
        }
      },
      "SnapshotResponse": {
        "type": "object",
        "required": [
          "hash",
          "url"
        ],
        "properties": {
          "hash": {
            "type": "string",
            "description": "Must equal the client's md5 or the client throws \"hash mismatch!\"."
          },
          "url": {
            "type": "string",
            "description": "Teacher-facing screenshot URL."
          }
        }
      },
      "ActivitySnapshotRequest": {
        "type": "object",
        "required": [
          "dataUrl",
          "seqId",
          "orgId",
          "subAccountId"
        ],
        "properties": {
          "dataUrl": {
            "type": "string",
            "description": "JPEG data URL",
            "quality 75.": null
          },
          "seqId": {
            "type": "number"
          },
          "orgId": {
            "type": "string"
          },
          "subAccountId": {
            "type": "string"
          }
        }
      },
      "LogsRequest": {
        "type": "object",
        "required": [
          "logs",
          "submissionTime"
        ],
        "properties": {
          "logs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "severity": {
                  "type": "string"
                },
                "log": {
                  "type": "string"
                },
                "stack_trace": {
                  "type": "string"
                },
                "account_id": {
                  "type": "string"
                },
                "extension_version": {
                  "type": "string"
                },
                "os": {
                  "type": "string"
                },
                "org_id": {
                  "type": "string"
                },
                "sub_account_id": {
                  "type": "string"
                }
              }
            }
          },
          "submissionTime": {
            "type": "number"
          }
        },
        "description": "Server re-serializes with Avro LogsBatch.Log (uncertainty N3)."
      },
      "X3Prediction": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string"
          },
          "probability": {
            "type": "number"
          }
        }
      },
      "X3ReportRequest": {
        "type": "object",
        "required": [
          "url",
          "title",
          "timestamp",
          "sources",
          "prediction",
          "triggerIds"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "imageId": {
            "type": "string",
            "nullable": true
          },
          "timestamp": {
            "type": "number"
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "text",
                "image"
              ]
            }
          },
          "prediction": {
            "type": "object",
            "properties": {
              "category": {
                "type": "string"
              },
              "confidence": {
                "type": "number"
              }
            }
          },
          "triggerIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "keywords": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "X3ReportResponse": {
        "type": "object",
        "properties": {
          "reportId": {
            "type": "string"
          },
          "contentDescriptor": {
            "type": "string"
          },
          "screenshotDescriptor": {
            "type": "string"
          },
          "imageDescriptor": {
            "type": "string"
          }
        }
      },
      "BeaconReportRequest": {
        "type": "object",
        "required": [
          "url",
          "timestamp",
          "prediction",
          "closedMessage",
          "model"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "timestamp": {
            "type": "number"
          },
          "prediction": {
            "type": "object",
            "additionalProperties": true
          },
          "closedMessage": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "didMessage": {
            "type": "boolean"
          },
          "seqId": {
            "type": "number"
          },
          "model": {
            "type": "string",
            "enum": [
              "FULLPAGE_PREDICTOR",
              "TV_SEARCH_PREDICTOR"
            ]
          }
        }
      },
      "BeaconReportResponse": {
        "type": "object",
        "properties": {
          "reportId": {
            "type": "string"
          },
          "contentDescriptor": {
            "type": "string"
          },
          "screenshotDescriptor": {
            "type": "string"
          }
        }
      },
      "X3Policy": {
        "type": "object",
        "properties": {
          "triggers": {
            "type": "object",
            "properties": {
              "messages": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/X3Trigger"
                }
              },
              "blocks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/X3Trigger"
                }
              }
            }
          },
          "blockedCategories": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Mapped internally to disabledCategories."
          },
          "disabledImageFilteringSiteCategories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "X3Trigger": {
        "type": "object",
        "properties": {
          "triggerId": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "threshold": {
            "type": "number"
          },
          "category": {
            "type": "string"
          },
          "ou": {
            "type": "string"
          },
          "triggerSources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "blockTarget": {
            "type": "string"
          }
        }
      },
      "BeaconPolicy": {
        "type": "object",
        "description": "Deployment/feature payload cached under key \"beacon\"; exact fields not reconstructed. HTTP 400 with code 9 means not deployed."
      },
      "BeaconPrediction": {
        "type": "object",
        "properties": {
          "phase": {
            "type": "string"
          },
          "probability": {
            "type": "number"
          }
        }
      },
      "SampleRequest": {
        "type": "object",
        "required": [
          "snapshotIds",
          "data"
        ],
        "properties": {
          "snapshotIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "filename",
                "dataURI"
              ],
              "properties": {
                "filename": {
                  "type": "string",
                  "enum": [
                    "screenshot.jpeg",
                    "content.json",
                    "prediction.json",
                    "metadata.json"
                  ]
                },
                "dataURI": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "EntityBatchPreAuthRecord": {
        "type": "object",
        "description": "INFORMATIONAL — the decoded Avro record inside the gzipped application/binary body of POST /v1/entitybatchpreauth (schema version 13). See EntityPreAuth for the per-entity payload.",
        "required": [
          "type",
          "compRand",
          "submissionTime",
          "extensionVersion",
          "payload"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ENTITIES"
            ]
          },
          "compRand": {
            "type": "string"
          },
          "submissionTime": {
            "type": "integer",
            "format": "int64"
          },
          "extensionVersion": {
            "type": "string"
          },
          "userAgent": {
            "type": "string"
          },
          "ip": {
            "type": "integer",
            "format": "int64",
            "description": "IPv4 packed as 256*(256*(256*a+b)+c)+d."
          },
          "deviceId": {
            "type": [
              "string",
              "null"
            ]
          },
          "payload": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EntityPreAuth"
            }
          }
        }
      },
      "EntityPreAuth": {
        "type": "object",
        "description": "INFORMATIONAL — per-entity Avro record. FlaggedActivity, searches (deprecated), videos, docs, blockedSite, viewings, tabs are the substantive surveillance fields; keyFlags is always [] as of src/1736; sourceType defaults to 0.",
        "properties": {
          "seqId": {
            "type": "integer",
            "format": "int64"
          },
          "url": {
            "type": "string"
          },
          "flaggedActivity": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "isNew": {
                  "type": "boolean"
                },
                "severity": {
                  "type": "number"
                },
                "image": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "categoryId": {
                  "type": "integer"
                },
                "flags": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "term": {
                        "type": "string"
                      },
                      "count": {
                        "type": "integer"
                      },
                      "severity": {
                        "type": "number"
                      },
                      "calculatedSeverity": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          },
          "keyFlags": {
            "type": "array",
            "description": "Always empty as of src/1736.",
            "items": {
              "type": "object"
            }
          },
          "searches": {
            "type": "array",
            "deprecated": true,
            "items": {
              "type": "object",
              "properties": {
                "isNew": {
                  "type": "boolean"
                },
                "searchTerms": {
                  "type": "string"
                }
              }
            }
          },
          "videos": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "isNew": {
                  "type": "boolean"
                },
                "title": {
                  "type": "string"
                },
                "uploader": {
                  "type": "string"
                },
                "categoryId": {
                  "type": "integer"
                },
                "rating": {
                  "type": "integer"
                },
                "description": {
                  "type": "string"
                },
                "channelId": {
                  "type": "string"
                },
                "category": {
                  "type": "string"
                },
                "uri": {
                  "type": "string"
                },
                "videoId": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "YOUTUBE"
                  ]
                },
                "blockMetadata": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string",
                          "enum": [
                            "ADMIN_YOUTUBE_CATEGORY",
                            "ADMIN_YOUTUBE_CHANNEL",
                            "ADMIN_YOUTUBE_KEYWORD",
                            "ADMIN_YOUTUBE_VIDEO",
                            "ADMIN_YOUTUBE_AGE_RESTRICTION",
                            "ADMIN_YOUTUBE_SAFE_MODE",
                            "UNKNOWN"
                          ]
                        },
                        "adminPolicyId": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "format": "int64"
                        },
                        "adminFilterId": {
                          "type": [
                            "integer",
                            "null"
                          ],
                          "format": "int64"
                        }
                      }
                    }
                  ]
                },
                "adminPolicyOverrideVideoId": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "format": "int64"
                }
              }
            }
          },
          "docs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "isNew": {
                  "type": "boolean"
                },
                "title": {
                  "type": "string"
                }
              }
            }
          },
          "faviconUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "tabId": {
            "type": [
              "integer",
              "null"
            ]
          },
          "blockedSite": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "isNew": {
                  "type": [
                    "boolean",
                    "null"
                  ]
                },
                "categoryId": {
                  "type": "integer"
                },
                "originalUrl": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "reason": {
                  "oneOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "string",
                      "enum": [
                        "BLOCK_WEB_PROXIES",
                        "BLOCK_DIRECT_IP_ACCESS",
                        "ADMIN_SITE_FILTER",
                        "ADMIN_SITE_CATEGORY_FILTER",
                        "ADMIN_SAFE_MODE",
                        "PARENT_SITE_FILTER",
                        "PARENT_PAUSE",
                        "PARENT_SCHEDULED_PAUSE",
                        "X3_REPORT",
                        "TEACHER_SCENE",
                        "UNKNOWN",
                        "BLOCK_CONSUMER_ACCOUNTS"
                      ]
                    }
                  ]
                },
                "adminPolicyId": {
                  "type": "integer",
                  "format": "int64"
                },
                "adminFilterId": {
                  "type": "integer",
                  "format": "int64"
                },
                "parentFilterId": {
                  "type": "integer",
                  "format": "int64"
                },
                "x3ReportPublicId": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "teacherSceneId": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "format": "int64"
                },
                "teacherSceneFilterId": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "format": "int64"
                }
              }
            }
          },
          "viewings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "startTimeMs": {
                  "type": "integer",
                  "format": "int64"
                },
                "endTimeMs": {
                  "type": "integer",
                  "format": "int64"
                },
                "isDirty": {
                  "type": "boolean"
                },
                "lastSubmissionTimeMs": {
                  "type": [
                    "integer",
                    "null"
                  ],
                  "format": "int64"
                }
              }
            }
          },
          "isNew": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "classroomIds": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "classroomSessionIds": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "isLocked": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "tabs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "faviconUrl": {
                  "type": "string"
                },
                "isActive": {
                  "type": "boolean"
                },
                "id": {
                  "type": "integer"
                }
              }
            }
          },
          "isProxy": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "sourceType": {
            "type": "integer",
            "default": 0
          },
          "tivanIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            }
          },
          "beaconIds": {
            "type": "array",
            "items": {
              "type": "integer",
              "format": "int64"
            }
          },
          "offTaskScores": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Keyed \"<classroomSessionId>-<timestamp>\"."
          },
          "dataScienceTags": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "clientSettings": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "type": "object",
                "properties": {
                  "admin": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  },
                  "teacher": {
                    "type": [
                      "boolean",
                      "null"
                    ]
                  }
                }
              }
            ]
          },
          "adminPolicyOverrideId": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64"
          },
          "x3ReportPublicIds": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "containsImageBlock": {
            "type": [
              "boolean",
              "null"
            ]
          }
        }
      }
    }
  }
}