{
  "info": {
    "name": "Trivia World SaaS - REST API v1",
    "description": "Official REST API documentation for Trivia World multi-tenant Examination & MCQ SaaS platform.\n\nPrimary client target: Android Mobile App.\nSecondary client target: Web application.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "http://localhost:3000",
      "type": "string"
    },
    {
      "key": "token",
      "value": "",
      "type": "string"
    },
    {
      "key": "cronSecret",
      "value": "",
      "type": "string"
    }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{token}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Auth",
      "item": [
        {
          "name": "Legacy Google callback redirect",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/auth/callback/google",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "auth",
                "callback",
                "google"
              ]
            },
            "description": "Compatibility route that preserves query parameters and redirects to /api/v1/auth/google/callback. New clients should use the versioned callback directly.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Request a password recovery link",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/forgot-password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "forgot-password"
              ]
            },
            "description": "Returns the same success message for known and unknown email addresses. Requires configured SMTP and public site base URL; rate limited to five requests per hour.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Set a new password using a recovery token",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/reset-password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "reset-password"
              ]
            },
            "description": "One-use recovery token expires after 30 minutes and revokes prior sessions after use; rate limited to ten requests per hour.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"sample_token\",\n  \"password\": \"replace-with-secret\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Read account security status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/security",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "security"
              ]
            },
            "description": "Works with a browser cookie or mobile Bearer session. Reports password availability, linked Google sign-in, 2FA state, and remaining recovery codes; never returns secrets."
          },
          "response": []
        },
        {
          "name": "Change an existing password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/password/change",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "password",
                "change"
              ]
            },
            "description": "Requires the current password. Available only when the account has a password. Other sessions and password reset links are revoked.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"currentPassword\": \"replace-with-secret\",\n  \"newPassword\": \"replace-with-secret\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Set up authenticator two-factor authentication",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/2fa/setup",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "2fa",
                "setup"
              ]
            },
            "description": "Authenticated user receives an otpauth QR data URL, URI, and secret for enrollment. The pending secret expires after ten minutes and does not affect the active factor. Keep these values private."
          },
          "response": []
        },
        {
          "name": "Verify and enable two-factor authentication",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/2fa/verify",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "2fa",
                "verify"
              ]
            },
            "description": "Submit the six-digit authenticator code before the pending setup expires. Eight one-use recovery codes are returned once; store them securely.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Disable authenticator two-factor authentication",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/2fa/disable",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "2fa",
                "disable"
              ]
            },
            "description": "Requires a current authenticator or unused recovery code; password accounts must also provide currentPassword.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Replace one-time recovery codes",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/2fa/recovery-codes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "2fa",
                "recovery-codes"
              ]
            },
            "description": "Requires an authenticator or unused recovery code; password accounts must also provide currentPassword. Invalidates all previous recovery codes and returns eight new codes once.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Finish a 2FA-protected Google sign-in",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/2fa/complete",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "2fa",
                "complete"
              ]
            },
            "description": "Web: Google callback sets a short-lived HttpOnly mfa_challenge cookie and redirects to /login?twoFactor=google; submit code here. Mobile: POST /api/v1/auth/google returns requires2FA and challengeToken; submit both here and receive the platform JWT only after verification. A recovery code can replace the six-digit TOTP code. Challenge expires after five minutes or five failed attempts.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Register new user account",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/register",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "register"
              ]
            },
            "description": "Creates global user identity and default student profile.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"password\": \"sample_password\",\n  \"fullName\": \"sample_fullName\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Login with email and password",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "login"
              ]
            },
            "description": "Authenticates credentials and handles 2FA challenges. Android clients should send a deviceId stable across reinstalls with the same app signing key and a human-readable deviceName. Device binding is scoped to the student account: one active device per student, with a cooldown before switching. Institution staff can reset a student's binding from their portal.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"user@example.com\",\n  \"password\": \"sample_password\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get current authenticated user session",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "me"
              ]
            },
            "description": "Get current authenticated user session"
          },
          "response": []
        },
        {
          "name": "Log out and permanently revoke database session",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "logout"
              ],
              "query": [
                {
                  "key": "all",
                  "value": "false",
                  "disabled": true
                }
              ]
            },
            "description": "Permanently deletes the active session token from the PostgreSQL database and clears the auth cookie. Pass ?all=true to revoke all active sessions for this account."
          },
          "response": []
        },
        {
          "name": "Initiate Google OAuth 2.0 Web sign-in flow",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/google",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "google"
              ]
            },
            "description": "Redirects the browser to Google's official OAuth consent screen using server-configured GOOGLE_CLIENT_ID and scopes. A short-lived state cookie protects the redirect callback.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Authenticate via Google ID Token or Access Token (Mobile & GIS)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/google",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "google"
              ]
            },
            "description": "Android: configure Google Sign-In with the server web OAuth client ID as the ID-token audience. Send the resulting ID token here in JSON. If 2FA is off, the response includes a Trivia World JWT. If 2FA is on, the response includes requires2FA and a five-minute challengeToken instead of a JWT; call POST /api/v1/auth/2fa/complete with that token, a TOTP or recovery code, and device metadata. Do not use the browser redirect endpoint for Android.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Google OAuth 2.0 redirect callback",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/google/callback?code=sample_code",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "google",
                "callback"
              ],
              "query": [
                {
                  "key": "code",
                  "value": "sample_code",
                  "disabled": false
                },
                {
                  "key": "error",
                  "value": "sample_error",
                  "disabled": true
                },
                {
                  "key": "state",
                  "value": "sample_state",
                  "disabled": true
                }
              ]
            },
            "description": "Validates the OAuth state cookie and exchanges the authorization code from Google. Accounts with 2FA enabled receive only a short-lived challenge cookie and are redirected to /login?twoFactor=google; a full session is created after POST /api/v1/auth/2fa/complete succeeds.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Super Admin Settings",
      "item": [
        {
          "name": "Test saved SMTP connection",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings/smtp-test",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings",
                "smtp-test"
              ]
            },
            "description": "SUPER_ADMIN only. Verifies saved SMTP connection and login without sending mail."
          },
          "response": []
        },
        {
          "name": "Purge expired audit logs now",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/audit/retention",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "audit",
                "retention"
              ]
            },
            "description": "SUPER_ADMIN only. Deletes audit records past the saved institution/system retention periods and writes a new audit event."
          },
          "response": []
        },
        {
          "name": "List platform users (ADMIN or SUPER_ADMIN)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/users",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "users"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                }
              ]
            },
            "description": "List platform users (ADMIN or SUPER_ADMIN)"
          },
          "response": []
        },
        {
          "name": "Manually grant institution subscription",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions/grant-subscription",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions",
                "grant-subscription"
              ]
            },
            "description": "SUPER_ADMIN or ADMIN. Sets any plan and 1-3650 days validity and writes an audit log with reason.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"planId\": \"sample_planId\",\n  \"durationDays\": 1,\n  \"reason\": \"sample_reason\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Ban or restore a user",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/users/:id/ban",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "users",
                ":id",
                "ban"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Administrators can manage USER accounts; SUPER_ADMIN can also manage ADMIN accounts. Banning revokes active sessions.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"banned\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Ban or restore an institution",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions/:id/ban",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions",
                ":id",
                "ban"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "A banned institution cannot use portal or student features until restored.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"banned\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get platform system settings",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings"
              ]
            },
            "description": "ADMIN and SUPER_ADMIN can read masked settings. SUPER_ADMIN additionally receives saved Supabase, R2 and Neon bucket names, plus decrypted storageKeys and paymentKeys for the settings form. Cache-Control is private, no-store."
          },
          "response": []
        },
        {
          "name": "Update system, storage, payment and recovery settings",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings"
              ]
            },
            "description": "SUPER_ADMIN only. Accepts a strict object of string-valued settings. Configures SMTP, public site base URL, manual transaction verification, bKash, Binance, storage, appearance and print QR options. Supabase, R2 and Neon buckets are configurable. Switching to R2 requires a public HTTPS URL. A storage switch affects new uploads; existing location-aware image keys keep their original provider and bucket. Pre-migration Neon images are captured before the first storage settings change. SMTP, Binance and storage credentials are encrypted at rest; the GET operation returns storage and Binance keys only to SUPER_ADMIN.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Test saved object storage credentials",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings/storage-test",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings",
                "storage-test"
              ]
            },
            "description": "SUPER_ADMIN only. Neon uploads and deletes a zero-byte test object. Supabase and R2 check bucket access. Saved credentials are used server-side.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"provider\": \"SUPABASE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Reveal saved storage keys on demand",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings/storage-credentials/reveal",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings",
                "storage-credentials",
                "reveal"
              ]
            },
            "description": "SUPER_ADMIN only. Returns the saved keys for one selected storage provider. The response is private and not cached.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"provider\": \"SUPABASE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Test saved payment gateway credentials",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/settings/payment-test",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "settings",
                "payment-test"
              ]
            },
            "description": "SUPER_ADMIN only. Checks the manual endpoint with HEAD or requests a bKash access token. This does not create a payment.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"provider\": \"MANUAL\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Platform Administration",
      "item": [
        {
          "name": "Permanently delete a rejected application",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/applications/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "applications",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Only rejected applications can be deleted, and the exact institution name is required for confirmation.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"confirmName\": \"sample_confirmName\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List pending and rejected institution applications",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/applications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "applications"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "20",
                  "disabled": true
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Approved applications are excluded. Returns applicants and pagination."
          },
          "response": []
        },
        {
          "name": "Approve or reject an institution application",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/applications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "applications"
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Approval creates an institution and owner membership, subject to the owner's institution creation entitlement.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"applicationId\": \"sample_applicationId\",\n  \"action\": \"APPROVE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List institutions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions"
              ],
              "query": [
                {
                  "key": "includeDeleted",
                  "value": "true",
                  "disabled": true
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Includes current subscription, plan, quota snapshot and usage counts. Deleted institutions are excluded unless includeDeleted=true."
          },
          "response": []
        },
        {
          "name": "View institution details",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Returns owner identities, location, contact and social fields, subscription and activity counts."
          },
          "response": []
        },
        {
          "name": "Change institution joining code",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Validates uniqueness and records an audit event.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete institution access",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/institutions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "institutions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Requires the exact institution name. Soft deletes the institution, bans access, and revokes all MCP keys while preserving payment and audit history.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"confirmName\": \"sample_confirmName\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "View user details",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/users/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "users",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Returns safe account details plus institution memberships and student enrollments. Password hashes and 2FA secrets are never returned."
          },
          "response": []
        },
        {
          "name": "Recover account or set system role",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/users/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "users",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "ADMIN can reset 2FA or an existing password for USER accounts; SUPER_ADMIN can manage privileged accounts and assign system roles. Actions require a reason, revoke sessions and write an audit event. A generated temporary password is returned once.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"action\": \"RESET_2FA\",\n  \"reason\": \"sample_reason\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List platform audit records",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/audit",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "audit"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "pageSize",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "scope",
                  "value": "ALL",
                  "disabled": true
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Paginated audit records across platform and institutions. Metadata is allowlisted to avoid exposing credential values."
          },
          "response": []
        },
        {
          "name": "Export platform audit records as CSV",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/audit/export?range=7",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "audit",
                "export"
              ],
              "query": [
                {
                  "key": "range",
                  "value": "7",
                  "disabled": false
                },
                {
                  "key": "from",
                  "value": "sample_from",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "sample_to",
                  "disabled": true
                },
                {
                  "key": "scope",
                  "value": "ALL",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Exports all matching records in a preset or inclusive custom UTC date range; metadata is allowlisted."
          },
          "response": []
        },
        {
          "name": "List purchase history",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/purchases",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "purchases"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "disabled": true
                },
                {
                  "key": "provider",
                  "value": "LEGACY",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "PENDING",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                }
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Paginated payment attempts, including failed and pending attempts. Filters are applied on the server."
          },
          "response": []
        },
        {
          "name": "Get platform dashboard statistics",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/stats",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "stats"
              ]
            },
            "description": "ADMIN or SUPER_ADMIN. Returns aggregate counts for users, institutions, seats, questions, pending applications and exams."
          },
          "response": []
        },
        {
          "name": "Notify all students",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "notifications"
              ]
            },
            "description": "ADMIN or SUPER_ADMIN only. Optional HTTPS image URL and destination type none, internal, website, or play_store. Never include private student data.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"title\": \"sample_title\",\n  \"message\": \"sample_message\",\n  \"destination\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Portal Operations",
      "item": [
        {
          "name": "Search registered users for staff assignment",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/team/search?institutionId=sample_institutionId&email=sample_email",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "team",
                "search"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "email",
                  "value": "sample_email",
                  "disabled": false
                }
              ]
            },
            "description": "Active institution owner or admin only; provide at least five email characters. Returns up to eight users with names and avatars. Rate limited to 60 searches per minute."
          },
          "response": []
        },
        {
          "name": "Edit batch details",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active institution owner or admin only.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"sample_name\",\n  \"code\": \"sample_code\",\n  \"description\": \"sample_description\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete an empty batch",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Only batches without students or exams may be deleted."
          },
          "response": []
        },
        {
          "name": "Get institution audit trail",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/audit?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "audit"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "pageSize",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "type",
                  "value": "sample_type",
                  "disabled": true
                }
              ]
            },
            "description": "Active institution member or SUPER_ADMIN. Paginated, searchable audit events; type filters targetType. MEMBER matches legacy Member and InstitutionMember, MCP matches InstitutionMcpKey."
          },
          "response": []
        },
        {
          "name": "Export own institution audit records as CSV",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/audit/export?institutionId=sample_institutionId&range=7",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "audit",
                "export"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "range",
                  "value": "7",
                  "disabled": false
                },
                {
                  "key": "from",
                  "value": "sample_from",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "sample_to",
                  "disabled": true
                },
                {
                  "key": "type",
                  "value": "sample_type",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                }
              ]
            },
            "description": "Active institution manager only. Exports all matching records in a preset or inclusive custom UTC date range."
          },
          "response": []
        },
        {
          "name": "Get institution dashboard",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/overview?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "overview"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Requires an active institution membership. Returns plan, quota usage, counts and recent questions."
          },
          "response": []
        },
        {
          "name": "List institution batches",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Returns batches with enrollment and exam counts."
          },
          "response": []
        },
        {
          "name": "Create a batch",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches"
              ]
            },
            "description": "Active OWNER, ADMIN or TEACHER; QUESTION_EDITOR cannot create batches.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"name\": \"sample_name\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List institution team",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/team?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "team"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Returns institution members and their user profiles."
          },
          "response": []
        },
        {
          "name": "Add a registered user to the team",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/team",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "team"
              ]
            },
            "description": "Active OWNER or ADMIN. The email must already belong to a registered, available account. Only OWNER can assign ADMIN.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"email\": \"user@example.com\",\n  \"role\": \"ADMIN\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Edit team member role or status",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/team",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "team"
              ]
            },
            "description": "Active OWNER can manage non-owner members; active ADMIN can manage teachers and editors but cannot assign or manage admins.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"memberId\": \"sample_memberId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Remove team member",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/team",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "team"
              ]
            },
            "description": "Active OWNER can remove non-owner members; active ADMIN can remove teachers and editors. Their MCP keys are deleted by database cascade.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"memberId\": \"sample_memberId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List subjects linked to a batch",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/subjects",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "subjects"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Requires active institution membership."
          },
          "response": []
        },
        {
          "name": "Replace batch subjects",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/subjects",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "subjects"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Accepts subjects belonging to the same institution. Cannot remove a subject required by a published exam.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"subjectIds\": [\n    \"sample_value\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List notification audiences",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/notifications?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "notifications"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Institution OWNER or ADMIN only. Returns batches and the institution notification switch."
          },
          "response": []
        },
        {
          "name": "Notify institution or batch students",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "notifications"
              ]
            },
            "description": "Institution OWNER or ADMIN only. Optional batchId and HTTPS image URL. No custom destination allowed. Disabled institution switch blocks sending.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"title\": \"sample_title\",\n  \"message\": \"sample_message\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Set institution notification switch",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions/:id/notification-permission",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions",
                ":id",
                "notification-permission"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Only an active institution OWNER may change this setting. When disabled, manual and automatic institution notifications are blocked.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"notificationsEnabled\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "API Documentation",
      "item": [
        {
          "name": "Open interactive API documentation",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/docs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "docs"
              ]
            },
            "description": "Returns the Scalar API reference and browser request console as HTML. Authorization is entered in the UI when calling protected endpoints.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Download OpenAPI specification",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/docs/openapi.json",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "docs",
                "openapi.json"
              ]
            },
            "description": "Returns this OpenAPI 3.0.3 document as JSON.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Download Postman collection",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/docs/postman.json",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "docs",
                "postman.json"
              ]
            },
            "description": "Downloads the checked-in Postman Collection v2.1. Regenerate it with scripts/export-postman-collection.ts after editing the OpenAPI specification.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Institutions & Academies",
      "item": [
        {
          "name": "Update institution profile and learning permissions",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active OWNER or ADMIN can change profile, branding, contact details, allowPracticeMode and allowStudyMode. Both permission flags default true; disabling either takes effect on the next student API request.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"description\": \"sample_description\",\n  \"primaryColor\": \"sample_primaryColor\",\n  \"accentColor\": \"sample_accentColor\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Switch student learning permissions immediately",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions/:id/learning-permissions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions",
                ":id",
                "learning-permissions"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active OWNER or ADMIN only. Send allowPracticeMode or allowStudyMode as a boolean; omitted permissions and institution profile fields remain unchanged. Student APIs enforce the saved value on their next request.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Correct and resubmit a rejected institution application",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions"
              ]
            },
            "description": "The applicant may update their rejected application and return it to pending review.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"applicationId\": \"sample_applicationId\",\n  \"name\": \"sample_name\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get current user's institution memberships",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions"
              ]
            },
            "description": "Returns active institution memberships, quotas, and the user's pending or rejected applications with review notes."
          },
          "response": []
        },
        {
          "name": "Request a new institution / academy",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "institutions"
              ]
            },
            "description": "Creates a PENDING application for admin review. The institution, OWNER membership, and starter plan are created only after approval. A user may have one pending application at a time; approval enforces the owner's creation entitlement.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Apex International Academy\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Student Practice",
      "item": [
        {
          "name": "List active assigned batches and subjects",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/batches",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "batches"
              ]
            },
            "description": "Returns batches from the authenticated student's active enrollments only. Each batch includes institution allowPracticeMode and allowStudyMode flags and active question counts for subjects assigned to that batch. No-store response."
          },
          "response": []
        },
        {
          "name": "Read assigned subject MCQs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/subjects/:id/study",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "subjects",
                ":id",
                "study"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "20",
                  "disabled": true
                },
                {
                  "key": "difficulty",
                  "value": "ALL",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                }
              ]
            },
            "description": "Requires active enrollment and batch assignment to this subject in the same institution. Returns active questions, answer keys and explanations. Institution allowStudyMode=false returns 403. Supports paginated text search and difficulty filtering; no-store response."
          },
          "response": []
        },
        {
          "name": "Get assigned subjects and mock-test permission flags",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/practice",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "practice"
              ]
            },
            "description": "Subjects come from active batch enrollments. Each subject includes allowPracticeMode; disabled subjects remain visible for UI explanation but cannot generate a test."
          },
          "response": []
        },
        {
          "name": "Generate randomized practice questions session",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/practice",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "practice"
              ]
            },
            "description": "Requires an active batch assignment and institution allowPracticeMode=true. Returns a random set of active questions with shuffled option positions, matching correctOptionKey and instant explanations. count must be an integer from 5 to 50. Disabled institutions return 403.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Institution MCP",
      "item": [
        {
          "name": "List MCP keys and active key allowance",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/integrations/mcp?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "integrations",
                "mcp"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Active institution OWNER or ADMIN. Key tokens are never returned after creation. MCP is active only on a paid plan with the feature enabled. used counts active, non-revoked keys; the package keyLimit is concurrent, not monthly."
          },
          "response": []
        },
        {
          "name": "Create a scoped MCP key",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/integrations/mcp",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "integrations",
                "mcp"
              ]
            },
            "description": "Active OWNER or ADMIN. Cannot exceed the subscription's active MCP key limit. Revoke an existing key to free a slot. Token is stored as a SHA-256 hash and the URL containing its secret is returned once. The key is bound to an active team member, permitted tools, and optionally subjects. Teacher and editor keys require explicit subjects.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"memberId\": \"sample_memberId\",\n  \"name\": \"sample_name\",\n  \"permissions\": [\n    \"subjects:read\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Edit MCP key permissions",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/integrations/mcp",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "integrations",
                "mcp"
              ]
            },
            "description": "Active OWNER or ADMIN. Updates key name, permissions and subject scope without changing the token or bound member. Member must still be active; key must not be revoked.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"keyId\": \"sample_keyId\",\n  \"name\": \"sample_name\",\n  \"permissions\": [\n    \"subjects:read\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Revoke an MCP key",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/integrations/mcp",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "integrations",
                "mcp"
              ]
            },
            "description": "Active OWNER or ADMIN. Revocation takes effect on the next MCP request.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"keyId\": \"sample_keyId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "MCP Streamable HTTP request",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/integrations/mcp/:token",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "integrations",
                "mcp",
                ":token"
              ],
              "variable": [
                {
                  "key": "token",
                  "value": "sample_token"
                }
              ]
            },
            "description": "Official MCP SDK endpoint. Supply a one-time URL issued by the owner; the path token authenticates an institution/member/permission/subject scope on every request. Supports MCP initialization, tools/list and tools/call. Tools: list_subjects, list_questions, create_questions, edit_questions, schedule_exam, subject_report, student_results_summary. The create_questions and edit_questions tools each accept a questions array of 1–50 items; edits require questionId per item. Batches are atomic and limited to 256 KiB per request. Question creation uses the plan's question bank capacity; there is no separate monthly MCP creation quota. Use application/json and Accept: application/json, text/event-stream as required by MCP clients.",
            "auth": {
              "type": "noauth"
            },
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "MCP stream or session request",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/integrations/mcp/:token",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "integrations",
                "mcp",
                ":token"
              ],
              "variable": [
                {
                  "key": "token",
                  "value": "sample_token"
                }
              ]
            },
            "description": "MCP protocol GET entry. Legacy stateless clients receive 405. The path token is checked on each request.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "MCP session termination",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/integrations/mcp/:token",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "integrations",
                "mcp",
                ":token"
              ],
              "variable": [
                {
                  "key": "token",
                  "value": "sample_token"
                }
              ]
            },
            "description": "MCP protocol session DELETE entry; this does not revoke the key. Use the owner portal DELETE endpoint to revoke.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Cron Jobs",
      "item": [
        {
          "name": "Expire elapsed subscriptions",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/expire-subscriptions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "expire-subscriptions"
              ]
            },
            "description": "Daily Vercel cron. Requires Authorization: Bearer CRON_SECRET. Marks elapsed ACTIVE or GRACE_PERIOD subscriptions EXPIRED; questions, students, payments and results are preserved. Entitlement checks use expiresAt immediately, even before cron runs.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Run subscription expiry maintenance",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/expire-subscriptions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "expire-subscriptions"
              ]
            },
            "description": "Same protected maintenance operation as GET, for external schedulers.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Permanently delete expired audit logs",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-audit-logs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-audit-logs"
              ]
            },
            "description": "Daily maintenance. Requires Authorization: Bearer CRON_SECRET. Uses separate saved retention periods for institution and system audit rows.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Run audit cleanup from an external scheduler",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-audit-logs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-audit-logs"
              ]
            },
            "description": "Same protected cleanup operation as GET.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Purge expired exam history",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-exam-history",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-exam-history"
              ]
            },
            "description": "Daily maintenance. Requires Authorization: Bearer CRON_SECRET. Deletes finished exams after their retentionDays elapsed, including attempts, scores, question snapshots and batch assignments. Retention is captured when an exam is published. The question bank remains.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Run exam history retention cleanup",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-exam-history",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-exam-history"
              ]
            },
            "description": "Same protected cleanup operation as GET for external schedulers.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Notify batches when automatic exam results become available",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/publish-exam-result-notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "publish-exam-result-notifications"
              ]
            },
            "description": "Notify batches when automatic exam results become available",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Purge expired session tokens and sign-in challenges from PostgreSQL",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-sessions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-sessions"
              ],
              "query": [
                {
                  "key": "key",
                  "value": "sample_key",
                  "disabled": true
                }
              ]
            },
            "description": "Automated cron endpoint that permanently removes expired sessions and two-factor sign-in challenges. Requires CRON_SECRET via Bearer token, x-cron-secret header, or ?key= query parameter.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Purge expired session tokens from PostgreSQL (POST webhook)",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/cron/cleanup-sessions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "cron",
                "cleanup-sessions"
              ],
              "query": [
                {
                  "key": "key",
                  "value": "sample_key",
                  "disabled": true
                }
              ]
            },
            "description": "Automated cron endpoint that permanently removes all expired session records from the database. Requires CRON_SECRET.",
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "{{cronSecret}}",
                  "type": "string"
                }
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Subscription Packages",
      "item": [
        {
          "name": "Get own payment attempt status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/attempt?attemptId=sample_attemptId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "attempt"
              ],
              "query": [
                {
                  "key": "attemptId",
                  "value": "sample_attemptId",
                  "disabled": false
                }
              ]
            },
            "description": "Returns the authenticated purchaser's payment attempt. Read-only; does not reconcile the gateway. No-store response."
          },
          "response": []
        },
        {
          "name": "Recheck a bKash payment",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/attempt",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "attempt"
              ]
            },
            "description": "Rate limited to five checks per minute. Server reconciles bKash payment before returning status. Manual transactions cannot be rechecked here.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"attemptId\": \"sample_attemptId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Institution billing status and payment quotes",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Institution billing status and payment quotes"
          },
          "response": []
        },
        {
          "name": "Legacy direct plan activation disabled",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing"
              ]
            },
            "description": "Legacy direct plan activation disabled",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Create bKash checkout for a paid plan",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/checkout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "checkout"
              ]
            },
            "description": "Active institution OWNER only. Server snapshots USD price, admin approved USD/BDT conversion rate, BDT amount and validity. The response URL is a gateway checkout; returning from it does not itself activate a subscription.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"planId\": \"sample_planId\",\n  \"provider\": \"BKASH\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "bKash browser return; server executes and queries payment before grant",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/bkash/callback",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "bkash",
                "callback"
              ],
              "query": [
                {
                  "key": "paymentID",
                  "value": "sample_paymentID",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "sample_status",
                  "disabled": true
                }
              ]
            },
            "description": "The gateway redirects the browser here. The server reconciles payment before redirecting to the billing page; a success query value alone never grants a subscription.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Verify a Send Money transaction and activate a paid package",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/manual",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "manual"
              ]
            },
            "description": "Active institution OWNER only. Amount and validity are calculated server-side. The transaction ID is unique and the subscription grant is idempotent.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"planId\": \"sample_planId\",\n  \"txnId\": \"sample_txnId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Start or verify a personal Binance Pay transfer",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/billing/binance",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "billing",
                "binance"
              ]
            },
            "description": "Active institution OWNER or ADMIN. Start snapshots the USD price as the same numeric amount in USDT and returns a Receive QR URL. Verify compares the submitted transaction ID against recent incoming Binance Pay history, the configured recipient, asset, amount and unique-use record before atomically granting the subscription. History is cached briefly; a missing payment may be retried without paying again.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"action\": \"start\",\n  \"institutionId\": \"sample_institutionId\",\n  \"planId\": \"sample_planId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get active public subscription packages",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/plans",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "plans"
              ]
            },
            "description": "Publicly accessible endpoint returning active subscription packages with features and pricing in USD ($).",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "List all plans for platform administration",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/plans",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "plans"
              ]
            },
            "description": "List all plans for platform administration"
          },
          "response": []
        },
        {
          "name": "Create or update a subscription package",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/plans",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "plans"
              ]
            },
            "description": "Create or update a subscription package",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"sample_name\",\n  \"slug\": \"sample_slug\",\n  \"maxStudents\": 1,\n  \"maxQuestions\": 1,\n  \"maxQuestionsPerTest\": 1,\n  \"resultRetentionDays\": 1,\n  \"adsEnabled\": true,\n  \"bulkImportEnabled\": true,\n  \"imageUploadsEnabled\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Permanently delete a subscription package",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/admin/plans?id=sample_id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "admin",
                "plans"
              ],
              "query": [
                {
                  "key": "id",
                  "value": "sample_id",
                  "disabled": false
                }
              ]
            },
            "description": "Deletes a package by ID if there are no active institution subscriptions attached to it."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Portal Exams",
      "item": [
        {
          "name": "List institution exams by period",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "view",
                  "value": "scheduled",
                  "disabled": true
                },
                {
                  "key": "year",
                  "value": "2000",
                  "disabled": true
                },
                {
                  "key": "subjectId",
                  "value": "sample_subjectId",
                  "disabled": true
                },
                {
                  "key": "sort",
                  "value": "newest",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                }
              ]
            },
            "description": "Active institution members. UTC period views: scheduled includes drafts and exams whose end time has not passed; this_month, last_month and year include finished published or archived exams. Returns 18 per page, available years, and the selected year's bulk-clear eligible count."
          },
          "response": []
        },
        {
          "name": "Create an exam",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams"
              ]
            },
            "description": "Active institution OWNER, ADMIN or TEACHER. Exam starts as a draft; duration defaults to 30 minutes. Only OWNER or ADMIN may set appOnly=true.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"title\": \"sample_title\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Clear finished exams in a year",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams"
              ]
            },
            "description": "Active institution OWNER, ADMIN or TEACHER. Permanently deletes finished published or archived exams in the selected UTC year only when they ended before the current UTC month. Current-month, future, live and draft exams are excluded even if requested directly. Cascades to attempts, results, snapshots and batch links; leaves question bank records.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"year\": 2000,\n  \"confirm\": \"DELETE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get an exam",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Returns exam, assigned batches, questions with resolved imageUrl, and attempt count to an active member."
          },
          "response": []
        },
        {
          "name": "Update an exam",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active OWNER, ADMIN or TEACHER. Only OWNER or ADMIN may change appOnly. Only supplied fields are changed. After the first attempt, only end-time extensions and result policies may change. Save settings before publishing; publishing requires at least one assigned batch.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Permanently delete one finished exam",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active institution OWNER, ADMIN or TEACHER. Exam must be published or archived and its end time must have passed. Deletes attempts, scores, snapshots and batch assignments; leaves question bank records.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"confirm\": \"DELETE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Replace exam batch assignments",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/batches",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "batches"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "An empty batchIds array removes student eligibility on a draft; published exams must retain at least one assigned batch. Active OWNER, ADMIN or TEACHER.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"batchIds\": [\n    \"sample_value\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Manage exam questions",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/questions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "questions"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "SET replaces the selected question set atomically and accepts an empty questionIds array; ADD appends questionIds; REMOVE requires questionId; RANDOMIZE requires count and optionally subjectId. Plan question cap is enforced.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"action\": \"ADD\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get exam submissions and ranking",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/results",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "results"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active institution member or SUPER_ADMIN. Returns exam summary and ranked submitted attempts within maxAttempts per student. Legacy excess attempts remain stored for audit but are excluded from official merit."
          },
          "response": []
        },
        {
          "name": "Review one submitted attempt",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/submissions/:attemptId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "submissions",
                ":attemptId"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                },
                {
                  "key": "attemptId",
                  "value": "sample_attemptId"
                }
              ]
            },
            "description": "Active institution member or SUPER_ADMIN. Includes question imageUrl, selected answers, correct answers, explanation and marks per question."
          },
          "response": []
        },
        {
          "name": "Validate exam before publishing",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/validate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "validate"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Checks schedule, selected subject, questions, at least one assigned batch and batch subject links without changing the exam."
          },
          "response": []
        },
        {
          "name": "Release or hide manually published results",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/exams/:id/release-results",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "exams",
                ":id",
                "release-results"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "OWNER, ADMIN or TEACHER. Exam must be published with MANUAL result policy and SCORE or REVIEW visibility. Writes an institution audit log.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"released\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Portal Questions",
      "item": [
        {
          "name": "Delete a question",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Requires active institution membership. Deletion lock can return QUESTION_DELETION_LOCKED with daysRemaining and unlockDate."
          },
          "response": []
        },
        {
          "name": "Get question for editing",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Get question for editing"
          },
          "response": []
        },
        {
          "name": "Edit question and replace or remove image",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Requires active institution membership. Validates subject and exactly four options with one correct answer, then reshuffles options. A replaced or removed image is deleted from storage after the question is saved.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"subjectId\": \"sample_subjectId\",\n  \"questionText\": \"sample_questionText\",\n  \"difficulty\": \"EASY\",\n  \"imageKey\": \"sample_imageKey\",\n  \"options\": [\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Search all institution questions for manual paper selection",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/print/manual?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "print",
                "manual"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "subjectId",
                  "value": "sample_subjectId",
                  "disabled": true
                },
                {
                  "key": "topicId",
                  "value": "sample_topicId",
                  "disabled": true
                },
                {
                  "key": "difficulty",
                  "value": "EASY",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                }
              ]
            },
            "description": "Returns 30 questions per page. Search and subject, topic, and difficulty filters are applied server-side; active institution membership is required."
          },
          "response": []
        },
        {
          "name": "Confirm ordered manual question selection",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/print/manual",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "print",
                "manual"
              ]
            },
            "description": "Validates that all selected questions are active and belong to the institution, then returns full printable questions in the requested order. Empty selection clears the paper. Maximum 500 questions.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"questionIds\": [\n    \"sample_value\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Load the latest printable question pool and answer key for institution staff",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/print?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "print"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Returns up to 500 active questions with newly shuffled option positions and matching answer flags, including older questions. The printed answer key starts on a separate A4 page. When enabled in admin settings and a Public base URL is configured, printQr is a QR image encoding the optional platform name, message, and website as multiline text. Only the QR image is printed."
          },
          "response": []
        },
        {
          "name": "Browse institution question bank",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "15",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "subjectId",
                  "value": "sample_subjectId",
                  "disabled": true
                },
                {
                  "key": "difficulty",
                  "value": "EASY",
                  "disabled": true
                }
              ]
            },
            "description": "Browse institution question bank"
          },
          "response": []
        },
        {
          "name": "Create single MCQ question",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions"
              ]
            },
            "description": "Requires four options and exactly one correct answer. The server shuffles options on save and reassigns position keys a–d while preserving the correct answer text.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"subjectId\": \"sample_subjectId\",\n  \"questionText\": \"sample_questionText\",\n  \"options\": [\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    },\n    {\n      \"key\": \"sample_key\",\n      \"text\": \"sample_text\",\n      \"isCorrect\": true\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Export active questions in one subject",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/export?institutionId=sample_institutionId&subjectId=sample_subjectId&format=csv",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "export"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "subjectId",
                  "value": "sample_subjectId",
                  "disabled": false
                },
                {
                  "key": "format",
                  "value": "csv",
                  "disabled": false
                }
              ]
            },
            "description": "Only an active institution OWNER or ADMIN can export questions in the requested subject. Returns a CSV or JSON attachment."
          },
          "response": []
        },
        {
          "name": "Bulk import questions from CSV/JSON parser",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/bulk",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "bulk"
              ]
            },
            "description": "Enforces quota integrity and atomicity via database transactions. Each imported question must have four options and one correct answer; options are shuffled on save.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"subjectId\": \"sample_subjectId\",\n  \"questions\": [\n    {\n      \"questionText\": \"sample_questionText\",\n      \"options\": [\n        {\n          \"key\": \"sample_key\",\n          \"text\": \"sample_text\",\n          \"isCorrect\": true\n        },\n        {\n          \"key\": \"sample_key\",\n          \"text\": \"sample_text\",\n          \"isCorrect\": true\n        },\n        {\n          \"key\": \"sample_key\",\n          \"text\": \"sample_text\",\n          \"isCorrect\": true\n        },\n        {\n          \"key\": \"sample_key\",\n          \"text\": \"sample_text\",\n          \"isCorrect\": true\n        }\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Upload MCQ image via active cloud storage driver",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/upload-image",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "upload-image"
              ]
            },
            "description": "Requires active institution membership and a paid plan with imageUploadsEnabled. Accepts client-compressed WebP within configured size and dimension limits, saves to the active storage provider, and returns a relative storage key.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "institutionId",
                  "type": "text",
                  "value": "sample_institutionId"
                },
                {
                  "key": "file",
                  "type": "file",
                  "src": ""
                }
              ]
            }
          },
          "response": []
        },
        {
          "name": "Remove an unsaved question image",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/questions/upload-image",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "questions",
                "upload-image"
              ]
            },
            "description": "Remove an unsaved question image",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"imageKey\": \"sample_imageKey\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Student Devices",
      "item": [
        {
          "name": "Get active student device",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/device",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "device"
              ]
            },
            "description": "Returns current binding and cooldown policy. Legacy singular endpoint; the /student/devices collection also exists."
          },
          "response": []
        },
        {
          "name": "Bind or verify student device",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/device",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "device"
              ]
            },
            "description": "May return DEVICE_COOLDOWN_LOCKED when attempting a change during the configured cooldown. Android should reuse a stable app-scoped device ID across reinstalls.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"deviceId\": \"sample_deviceId\",\n  \"deviceName\": \"sample_deviceName\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Revoke current student device",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/device",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "device"
              ]
            },
            "description": "Cooldown policy may prevent reset; returns 403 until reset is allowed."
          },
          "response": []
        },
        {
          "name": "Get student's active bound device and cooldown status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/devices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "devices"
              ]
            },
            "description": "Get student's active bound device and cooldown status"
          },
          "response": []
        },
        {
          "name": "Bind or verify current student device",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/devices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "devices"
              ]
            },
            "description": "Enforces single registered active device per student account with 7-day cooldown policy.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"deviceId\": \"android-v1:9e31...\",\n  \"deviceName\": \"Samsung Galaxy S24\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Revoke active device registration (after cooldown)",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/devices",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "devices"
              ]
            },
            "description": "Revoke active device registration (after cooldown)"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Student Bootstrap & Profile",
      "item": [
        {
          "name": "Get student profile",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "profile"
              ]
            },
            "description": "Returns student profile, current device and enrollment details."
          },
          "response": []
        },
        {
          "name": "Update student profile",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "profile"
              ]
            },
            "description": "Only provided fields are changed. Student may set rollNumber for the active institution once when empty; after that only institution staff can change or clear it. Roll values are trimmed, at most 50 characters, and cannot contain control characters. targetExam and phoneNumber may be cleared with null.",
            "body": {
              "mode": "raw",
              "raw": "{}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get student dashboard",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/overview",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "overview"
              ]
            },
            "description": "Returns practice statistics, current enrollments, pending join requests and recent attempts. Banned institutions are excluded."
          },
          "response": []
        },
        {
          "name": "Initialize student mobile app or web session",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/bootstrap",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "bootstrap"
              ]
            },
            "description": "Creates a student profile for a newly signed-in account if needed, then returns active institution branding, bound device status, and ad policy. General practice remains available without institution enrollment."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Student Institutions",
      "item": [
        {
          "name": "Set an empty institution roll number",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/institutions/:id/roll",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "institutions",
                ":id",
                "roll"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "The authenticated student can set their own PENDING or ACTIVE institution enrollment roll once, only while it is empty. Roll values are trimmed, 1–50 characters, and cannot contain control characters. Institution staff can change it later via PATCH /portal/students.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"rollNumber\": \"sample_rollNumber\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List authorized notification topics",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/notification-topics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "notification-topics"
              ]
            },
            "description": "Android calls this after login, on app launch, and after token or enrollment changes. The app subscribes and unsubscribes locally. Returns all-students, active institution, and assigned batch topics; no device identifier is stored."
          },
          "response": []
        },
        {
          "name": "Synchronize this Firebase registration's topics",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/notification-topics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "notification-topics"
              ]
            },
            "description": "Web push: send the browser FCM registration ID to reconcile server-side subscriptions; this requires Firebase topic-management IAM permissions. Older Android clients marked X-Trivia-Client: android receive only the authorized topic list and subscribe locally. The identifier is used only during this request.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"registrationId\": \"sample_registrationId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Remove this browser's notification topics",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/notification-topics",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "notification-topics"
              ]
            },
            "description": "Web push: call before logout or account switch with the locally held FCM registration ID. Android unsubscribes locally; older Android clients marked X-Trivia-Client: android receive a no-op success.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"registrationId\": \"sample_registrationId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Leave an assigned batch",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/batches/:id/leave",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "batches",
                ":id",
                "leave"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Student leaves their own active batch assignment. Resync notification topics after success."
          },
          "response": []
        },
        {
          "name": "Leave an institution",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/institutions/:id/leave",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "institutions",
                ":id",
                "leave"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Student leaves their own active institution enrollment and all its batches. Resync notification topics after success."
          },
          "response": []
        },
        {
          "name": "Request institution enrollment by code",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/enroll",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "enroll"
              ]
            },
            "description": "Creates a PENDING enrollment. rollNumber is optional, trimmed, limited to 50 characters and cannot contain control characters. It belongs to this institution enrollment. Institution staff must approve before access or seat locking begins.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"sample_code\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List institution students and pending requests, including UID, roll number and batch assignments",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/students?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "students"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "disabled": true
                },
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "status",
                  "value": "ALL",
                  "disabled": true
                },
                {
                  "key": "batchId",
                  "value": "sample_batchId",
                  "disabled": true
                }
              ]
            },
            "description": "List institution students and pending requests, including UID, roll number and batch assignments"
          },
          "response": []
        },
        {
          "name": "Approve, reject, or remove an enrollment",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/students",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "students"
              ]
            },
            "description": "Approval changes PENDING to ACTIVE and applies the configured seat lock. Removal is denied during the lock.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"enrollmentId\": \"sample_enrollmentId\",\n  \"action\": \"APPROVE\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Set, change or clear a student's institution roll number",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/students",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "students"
              ]
            },
            "description": "Active OWNER, ADMIN or TEACHER can update the roll number on an enrollment at any time. Values are trimmed, limited to 50 characters and cannot contain control characters.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"enrollmentId\": \"sample_enrollmentId\",\n  \"rollNumber\": \"sample_rollNumber\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Assign or unassign an approved student to a batch",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/assign",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                "assign"
              ]
            },
            "description": "Assign or unassign an approved student to a batch",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"batchId\": \"sample_batchId\",\n  \"enrollmentId\": \"sample_enrollmentId\",\n  \"action\": \"ASSIGN\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List batch candidates or current members",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/students",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "students"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ],
              "query": [
                {
                  "key": "search",
                  "value": "sample_search",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "mode",
                  "value": "available",
                  "disabled": true
                }
              ]
            },
            "description": "Active institution owner, admin, or teacher. Use mode=available with at least 5 name or email characters to search approved candidates; mode=assigned lists current batch members and allows browsing without search."
          },
          "response": []
        },
        {
          "name": "Add approved students to a batch in bulk",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/students",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "students"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Validates active institution enrollment and existing assignments for every selected student. Returns added, alreadyAssigned, and ineligible counts.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"enrollmentIds\": [\n    \"sample_value\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Remove selected students from a batch",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/students",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "students"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active institution owner, admin, or teacher. Removes only batch assignments; institution enrollment and other batches remain intact. Returns removed and notAssigned counts.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"enrollmentIds\": [\n    \"sample_value\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Export a batch's assigned students as CSV",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/batches/:id/students/export",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "batches",
                ":id",
                "students",
                "export"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Active institution owner, admin, or teacher. Streams all assigned students with contact, roll, status, and enrollment dates. CSV cells are escaped for spreadsheet safety."
          },
          "response": []
        },
        {
          "name": "List all institutions enrolled or applied to",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "institutions"
              ]
            },
            "description": "Returns each enrollment status, assigned batches and published exam count for the authenticated student. A new account receives an empty list. Only active enrollments grant exam access."
          },
          "response": []
        },
        {
          "name": "Join new institution by public joining code",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "institutions"
              ]
            },
            "description": "Submits admission request using joining code (e.g. 'APEX123').",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"code\": \"APEX123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Switch student's active institution",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/institutions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "institutions"
              ]
            },
            "description": "Switch student's active institution",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Portal Students",
      "item": [
        {
          "name": "Reset an enrolled student's device lock",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/students/device",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "students",
                "device"
              ]
            },
            "description": "Active institution OWNER, ADMIN or TEACHER may reset an ACTIVE student's account-wide device binding only when the actor is not the student and both the student's approved enrollment and the staff membership predate the active device binding. This does not remove the enrollment or seat lock. The student binds a device on their next app sign-in.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"enrollmentId\": \"sample_enrollmentId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Student Exams",
      "item": [
        {
          "name": "Get assigned exam briefing before starting",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Returns schedule, serverTime, state, instructions, question count, scoring, timer, attempt, result and proctoring rules without creating an attempt or exposing answer keys. Show this briefing and require an explicit student action before calling start. Unassigned, unpublished and app-only exams requested from the web return 404."
          },
          "response": []
        },
        {
          "name": "Advance per-question timer",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/advance",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "advance"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Only for QUESTION timer mode. Marks an expired current question as timed out, then moves to the next question or submits the attempt.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"attemptId\": \"sample_attemptId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "List exams assigned to the authenticated student",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": true
                },
                {
                  "key": "institutionCode",
                  "value": "sample_institutionCode",
                  "disabled": true
                },
                {
                  "key": "state",
                  "value": "UPCOMING",
                  "disabled": true
                }
              ]
            },
            "description": "Returns only published exams with at least one assigned batch matching the student's active batch enrollment, newest creation first with createdAt. App-only exams remain visible on web for countdown and show appOnly=true, canParticipate=false and appDownloadUrl; the web cannot start or continue them. Filters by institutionId, institutionCode and state. Use startAt and serverTime for an upcoming countdown; the server still checks the schedule at start. Client channel: send X-Trivia-Client: android and X-Trivia-Device-Id with a Bearer token from a registered device for app-only exams. The marker is not cryptographic app attestation. Hidden scores are omitted."
          },
          "response": []
        },
        {
          "name": "Start or resume an exam attempt",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/start",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "start"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Call only after displaying the exam briefing and receiving explicit student confirmation. Checks active enrollment, a nonempty assigned batch intersection, start/end window, app-only client access and attempt limit. Future exams return 403 with startAt. App-only requests require Bearer authentication, X-Trivia-Client: android and X-Trivia-Device-Id matching an active registered device. A header/device identifier alone cannot prove the caller is the genuine Android app; cryptographic app attestation is a separate integration. Concurrent starts are serialized. Creates immutable question snapshots and returns server deadlines without answer keys. Resuming never resets the timer."
          },
          "response": []
        },
        {
          "name": "Real-time answer selection recording",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/save-answer",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "save-answer"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Real-time answer selection recording",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"attemptId\": \"sample_attemptId\",\n  \"questionId\": \"sample_questionId\",\n  \"selectedOptionKey\": \"a\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Report tab-switch or window blur violation",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/warning",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "warning"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Report tab-switch or window blur violation",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"attemptId\": \"sample_attemptId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Final exam submission and scoring",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/submit",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "submit"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ]
            },
            "description": "Final exam submission and scoring",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"attemptId\": \"sample_attemptId\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get student's exam score, merit rank, and answer review",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/student/exams/:id/results",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "student",
                "exams",
                ":id",
                "results"
              ],
              "variable": [
                {
                  "key": "id",
                  "value": "sample_id"
                }
              ],
              "query": [
                {
                  "key": "attemptId",
                  "value": "sample_attemptId",
                  "disabled": true
                }
              ]
            },
            "description": "Get student's exam score, merit rank, and answer review"
          },
          "response": []
        }
      ]
    },
    {
      "name": "Public Settings",
      "item": [
        {
          "name": "Get public site settings and dynamic brand theme colors",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/settings/public",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "settings",
                "public"
              ]
            },
            "description": "Includes settings.public_show_ads, the current public Android ad eligibility flag. Mobile clients must also obtain ad consent before loading a placement.",
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Subjects & Categories",
      "item": [
        {
          "name": "Get institution subjects",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/subjects?institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "subjects"
              ],
              "query": [
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Fetch all academic subjects and categories configured for an institution."
          },
          "response": []
        },
        {
          "name": "Create a new subject",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/subjects",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "subjects"
              ]
            },
            "description": "Add a subject category to the institution's question repository.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"institutionId\": \"sample_institutionId\",\n  \"name\": \"Physics\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Delete an empty subject",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/v1/portal/subjects?id=sample_id&institutionId=sample_institutionId",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "portal",
                "subjects"
              ],
              "query": [
                {
                  "key": "id",
                  "value": "sample_id",
                  "disabled": false
                },
                {
                  "key": "institutionId",
                  "value": "sample_institutionId",
                  "disabled": false
                }
              ]
            },
            "description": "Delete an academic subject if it contains 0 questions."
          },
          "response": []
        }
      ]
    }
  ]
}
