{
  "schema_version": 1,
  "title": "bugAgent API reference index",
  "description": "Machine-readable index of the endpoint cards in the public API reference. This is not an OpenAPI schema.",
  "documentation_url": "https://bugagent.com/api-reference",
  "endpoints": [
    {
      "id": "post-auth-register",
      "method": "POST",
      "path": "/api/auth/register",
      "audience": "public",
      "authentication": "Public",
      "description": "Register a new agent account. Accounts created via this endpoint are automatically flagged as `is_agent: true`. A unique workspace is created for each new account."
    },
    {
      "id": "post-auth-login",
      "method": "POST",
      "path": "/api/auth/login",
      "audience": "public",
      "authentication": "Public",
      "description": "Sign in and receive a JWT access token. Use the returned token as a Bearer token for subsequent requests, or generate a long-lived API key instead."
    },
    {
      "id": "get-reports",
      "method": "GET",
      "path": "/api/reports",
      "audience": "api_key",
      "scope": "reports:read",
      "authentication": "reports:read",
      "description": "List bug reports for the authenticated user's account and team. Project and workspace filters are applied before pagination. Each report includes `project_id`, `project`, `short_id`, `legacy_short_id`, and `project_short_id` so API clients can link the report without guessing project context."
    },
    {
      "id": "post-reports",
      "method": "POST",
      "path": "/api/reports",
      "audience": "api_key",
      "scope": "reports:write",
      "authentication": "reports:write",
      "description": "Create a new report. Supports bugs, feature requests, enhancements, technical debt, and more. If `type` is omitted, bug Agent auto-classifies based on title and description. The response returns both the workspace-scoped legacy ID and the project-scoped short ID, along with `project_id` and project metadata."
    },
    {
      "id": "post-reports-format",
      "method": "POST",
      "path": "/api/reports/format-description",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Reformat a bug report description into a structured bug template using AI. Returns the formatted description text without modifying the report. Use this to preview the AI formatting before saving."
    },
    {
      "id": "get-reports-id",
      "method": "GET",
      "path": "/api/reports/:id",
      "audience": "api_key",
      "scope": "reports:read",
      "authentication": "reports:read",
      "description": "Retrieve a single bug report by ID. Returns 404 if the report doesn't exist or you don't have access."
    },
    {
      "id": "get-reports-id-children",
      "method": "GET",
      "path": "/api/reports/:id/children",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List one page of child reports for an Epic. The Epic must belong to a project the caller can access. Aggregate progress is calculated in PostgreSQL and does not require loading every child report."
    },
    {
      "id": "patch-reports-id",
      "method": "PATCH",
      "path": "/api/reports/:id",
      "audience": "api_key",
      "scope": "reports:write",
      "authentication": "reports:write",
      "description": "Update a bug report. Only fields you include in the body are updated."
    },
    {
      "id": "delete-reports-id",
      "method": "DELETE",
      "path": "/api/reports/:id",
      "audience": "api_key",
      "scope": "reports:write",
      "authentication": "reports:write",
      "description": "Permanently delete one report and its stored attachments. The identifier may be the canonical UUID or a short ID accepted by the report resolver. The caller must be able to access the report's workspace and project. Treat `404` as missing or unauthorized."
    },
    {
      "id": "post-reports-assign",
      "method": "POST",
      "path": "/api/reports/assign",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Assign a bug report to a team member. A database trigger automatically fires the in-app bell notification AND an email to the new assignee whenever the assignee actually changes (re-saving the same user is silent). Same trigger pipeline is reached by every other update path (PATCH /api/reports/:id, the MCP `update_bug_report` tool, raw SQL) so notifications are consistent regardless of how the assignment happened."
    },
    {
      "id": "post-reports-links",
      "method": "POST",
      "path": "/api/reports/links",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a directional semantic link between two bug reports in the same workspace - the user-curated counterpart to the auto-detected `similar_reports` field. Both reports must belong to the same team; cross-team links are rejected (the database trigger enforces this as well as the API check). The inverse perspectives (`duplicated-by` / `subtask-of` / `blocks` / `blocks-testing`) are not stored separately - they're derived at read time from the same row."
    },
    {
      "id": "delete-reports-links",
      "method": "DELETE",
      "path": "/api/reports/links",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Remove a previously-created bug-report link by its UUID. The caller must be an active member of the workspace that owns the link."
    },
    {
      "id": "post-reports-upload",
      "method": "POST",
      "path": "/api/reports/upload",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Upload file attachments (screenshots, screen recordings, audio memos, documents) to a bug report. Uses `multipart/form-data`."
    },
    {
      "id": "post-reports-upload-preflight",
      "method": "POST",
      "path": "/api/reports/upload-preflight",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Upload a single image before a report ID exists, so it can be embedded inline in the bug description as a markdown image reference. Used by the \"Report a Bug\" composer when pasting a screenshot — the returned URL is inserted at the cursor position as `![filename](url)`. Files land at a preflight storage path and are not linked to any bug report row; orphaned files are cleaned up by a periodic storage sweep. Max 25 MB."
    },
    {
      "id": "post-reports-flush",
      "method": "POST",
      "path": "/api/reports/flush",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Bulk delete old bug reports. Requires `owner` or `admin` team role."
    },
    {
      "id": "post-comments",
      "method": "POST",
      "path": "/api/reports/comments",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Add a comment to a bug report, or toggle an emoji reaction on a comment."
    },
    {
      "id": "patch-comments",
      "method": "PATCH",
      "path": "/api/reports/comments",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Edit a comment. Only the author can edit their own comments."
    },
    {
      "id": "delete-comments",
      "method": "DELETE",
      "path": "/api/reports/comments",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a comment. Only the author can delete their own comments."
    },
    {
      "id": "get-projects",
      "method": "GET",
      "path": "/api/projects",
      "audience": "api_key",
      "scope": "reports:read",
      "authentication": "reports:read",
      "description": "List all accessible projects in the key-bound or active workspace. Each project includes `id`, `team_id` (the workspace UUID), `name`, `slug`, `ticket_prefix`, `description`, `is_default`, and `created_at`. Use the exact workspace and project UUIDs with `GET /api/reports`, `POST /api/reports`, and MCP project filters."
    },
    {
      "id": "post-projects",
      "method": "POST",
      "path": "/api/projects",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new project. The first project is automatically set as default. Subject to plan limits."
    },
    {
      "id": "patch-projects",
      "method": "PATCH",
      "path": "/api/projects",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update a project's name, description, or default status."
    },
    {
      "id": "delete-projects",
      "method": "DELETE",
      "path": "/api/projects",
      "audience": "dashboard_session",
      "authentication": "Owner / Manager",
      "description": "Permanently delete a project and all associated data . Only owners and managers can delete projects. You cannot delete your last project - every workspace must have at least one."
    },
    {
      "id": "get-keys",
      "method": "GET",
      "path": "/api/keys",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List active, non-revoked workspace API keys. Requires an authenticated dashboard session and an owner, administrator, or manager role. API keys cannot manage other keys. Only the key prefix is returned — full keys are shown once at creation."
    },
    {
      "id": "post-keys",
      "method": "POST",
      "path": "/api/keys",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Generate a new API key. Requires an authenticated dashboard session and an owner, administrator, or manager role. API keys cannot create other keys. The full key (starting with `ba_live_`) is returned only once — store it securely. Free workspaces may keep at most two active workspace API keys."
    },
    {
      "id": "delete-keys-id",
      "method": "DELETE",
      "path": "/api/keys/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Revoke an API key. Requires an authenticated dashboard session and an owner, administrator, or manager role. The key is soft-deleted and can no longer be used for authentication."
    },
    {
      "id": "post-keys-regenerate",
      "method": "POST",
      "path": "/api/keys/:id/regenerate",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Revoke the current key and generate a new one with the same name and scopes. Requires an authenticated dashboard session and an owner, administrator, or manager role. Returns the new full key and immediately invalidates the previous secret."
    },
    {
      "id": "get-profile",
      "method": "GET",
      "path": "/api/profile",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get the authenticated user's profile."
    },
    {
      "id": "patch-profile",
      "method": "PATCH",
      "path": "/api/profile",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update your profile. Only `full_name` can be changed via the API (security: no email, role, or plan changes)."
    },
    {
      "id": "post-profile-password",
      "method": "POST",
      "path": "/api/profile/password",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Change your password."
    },
    {
      "id": "get-settings",
      "method": "GET",
      "path": "/api/settings",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get your profile info and notification preferences."
    },
    {
      "id": "patch-settings",
      "method": "PATCH",
      "path": "/api/settings",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update notification preferences."
    },
    {
      "id": "get-usage",
      "method": "GET",
      "path": "/api/usage",
      "audience": "api_key",
      "scope": "usage:read",
      "authentication": "usage:read",
      "description": "Get your current plan usage (reports used, limit, remaining, reset date)."
    },
    {
      "id": "get-stats",
      "method": "GET",
      "path": "/api/stats",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get report statistics with daily counts, breakdowns by type, severity, and status."
    },
    {
      "id": "post-checkout",
      "method": "POST",
      "path": "/api/checkout",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Deprecated compatibility endpoint. Enterprise enrollment is sales-assisted, so this endpoint never creates a checkout session or changes a workspace plan."
    },
    {
      "id": "post-billing-portal",
      "method": "POST",
      "path": "/api/billing-portal",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get a URL to the Stripe billing portal where users can manage subscriptions, payment methods, and invoices."
    },
    {
      "id": "post-invite",
      "method": "POST",
      "path": "/dashboard/settings/team",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Invite a user to your team. Sends an email invitation with a 5-day expiry. Invited users join the inviting workspace directly — no separate workspace is created."
    },
    {
      "id": "post-edit-name",
      "method": "POST",
      "path": "/dashboard/settings/team",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Edit a team member's display name inline. Owners and admins can edit any non-owner member. Managers can edit contributors and other managers but not owners or admins."
    },
    {
      "id": "post-change-role",
      "method": "POST",
      "path": "/dashboard/settings/team",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Change a team member's role. Managers can assign contributor or manager roles. Only owners/admins can assign admin. Owner role can only be changed via ownership transfer."
    },
    {
      "id": "post-remove-member",
      "method": "POST",
      "path": "/dashboard/settings/team",
      "audience": "dashboard_session",
      "authentication": "Owner / Manager",
      "description": "Remove a team member from the workspace. Only owners and managers can remove members. Owners cannot be removed. All data created by the removed user (bug reports, automations, test cases, mobile apps, geo snaps, schedules, time tracking) is reassigned to the person performing the removal. Notes are deleted. The user's profile is preserved as a ghost stub for foreign-key integrity and re-invite capability."
    },
    {
      "id": "post-switch-team",
      "method": "POST",
      "path": "/api/switch-team",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Switch the active workspace context. Users who belong to multiple workspaces use this to change which workspace's data they see."
    },
    {
      "id": "post-create-team",
      "method": "POST",
      "path": "/api/create-team",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new workspace. A user may own up to five workspaces; invited memberships do not count toward this limit. Every additional workspace starts on Free, regardless of the creator's other workspace plans. Use Feedback or Talk to Sales to request Enterprise enrollment or additional workspaces. If no name is provided, an uplifting name is auto-generated (e.g. \"Radiant Foxes\"). A Default Project is automatically created so the workspace always has at least one project."
    },
    {
      "id": "get-jira-connect",
      "method": "GET",
      "path": "/api/jira/connect",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Start the Jira OAuth 2.0 flow on either bugAgent plan. Redirects to Atlassian's authorization page. The resulting connection is stored against the initiating active workspace, not the individual user, and the callback fails if the active workspace changes during authorization."
    },
    {
      "id": "get-jira-projects",
      "method": "GET",
      "path": "/api/jira/projects",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Fetch all Jira projects accessible by the team's shared connection. Restricted to workspace owners, admins, and managers — this browses the connected Jira site, which ordinary members should not see. Returns the default project key if one is saved."
    },
    {
      "id": "post-jira-sync",
      "method": "POST",
      "path": "/api/jira/sync",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Sync a bug report to Jira using the team's shared connection. Creates a Jira issue with mapped fields, labels, and uploads any attachments. Severity is mapped to Jira priority (`s1`/`critical`->Highest, `s2`/`high`->High, `s3`/`medium`->Medium, `s4`/`low`->Low). Any authenticated team member can sync."
    },
    {
      "id": "post-jira-force-sync",
      "method": "POST",
      "path": "/api/jira/force-sync",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Force an immediate bi-directional sync between bugAgent and Jira. Triggered by the sync button next to the AUTO SYNC badge on the report detail page. Syncs description, title, severity/priority, comments, and media attachments."
    },
    {
      "id": "get-jira-check",
      "method": "GET",
      "path": "/api/jira/check",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Check if a synced Jira issue has been modified. Compares title, severity, status, and type between bugAgent and Jira. Severity uses last-updated-wins logic and is auto-applied during polling. Comments and attachments are synced bi-directionally via the auto-sync polling and force-sync endpoints."
    },
    {
      "id": "post-jira-merge",
      "method": "POST",
      "path": "/api/jira/merge",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Bi-directional merge between bugAgent and Jira. Updates bugAgent with the chosen values, then pushes them to Jira so both systems are identical. Severity is mapped to Jira priority. For automatic conflict resolution, use `/api/jira/force-sync` which applies last-updated-wins logic."
    },
    {
      "id": "post-jira-settings",
      "method": "POST",
      "path": "/api/jira/settings",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Update the team's Jira integration settings or disconnect. Only managers and above can modify these settings."
    },
    {
      "id": "jira-mappings",
      "method": "GET",
      "path": "/api/jira/mappings",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List the workspace's project mappings — which Jira project each bugAgent project syncs to. Sync resolution order: explicit key on the push, then the report's project mapping, then the default project key."
    },
    {
      "id": "post-jira-mappings",
      "method": "POST",
      "path": "/api/jira/mappings",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Create, update, or remove a project mapping. Sending an empty `jira_project_key` removes the mapping and the project falls back to the default. Mappings are removed automatically when the Jira connection is disconnected or reconnected to a different site."
    },
    {
      "id": "get-jira-project-meta",
      "method": "GET",
      "path": "/api/jira/project-meta",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "The live value-spaces of one Jira project — its workflow statuses and the site's priorities. Feeds the field-mapping modal. Query: `projectKey`."
    },
    {
      "id": "post-jira-bulk-sync",
      "method": "POST",
      "path": "/api/jira/bulk-sync",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "The explicit \"Sync everything now\" action. Requires at least one mapped project whose effective mode is `auto_all` (per-project override, else the workspace default); only those projects join the merge. It never runs on its own. Actions: `preview` (dry-run counts, nothing written), `start` (creates the job and kicks a server-side run — one running job per workspace, and it is safe to close the page: the server drives it to completion, and a watchdog revives runs interrupted by a deploy), `status` (live progress — the dashboard polls this to show a run in flight, and stamps when the last full sync finished), `cancel`, `tick` (processes one batch under the same lease; kept for compatibility, no longer required). Jira issues already linked to a report are updated, never duplicated; unlinked issues become reports translated through the project's field map; unlinked reports push to Jira. Nothing is deleted on either side."
    },
    {
      "id": "post-jira-suggest-field-map",
      "method": "POST",
      "path": "/api/jira/suggest-field-map",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "AI-assisted field mapping: send the Jira project's status and priority names (never ticket content) and receive a proposed mapping, validated server-side against both value sets. The proposal prefills the modal for review — it is never applied automatically."
    },
    {
      "id": "post-jira-batch-sync-status",
      "method": "POST",
      "path": "/api/jira/batch-sync-status",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Batch-sync report statuses with Jira. Used by the Kanban board to push status changes after drag-and-drop reordering. Fetches the latest Jira status for each report and reconciles any differences."
    },
    {
      "id": "post-automations",
      "method": "POST",
      "path": "/api/automations",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Deprecated compatibility endpoint. Browser recording now runs in the authenticated Automate Web workspace. Use `POST /api/automations/create` to create an automation from a Playwright script."
    },
    {
      "id": "post-automations-create",
      "method": "POST",
      "path": "/api/automations/create",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new automation with a custom Playwright script. Unlike `POST /automations` which expects recorded FAB steps, this endpoint lets you supply a script directly - ideal for hand-written tests, AI-generated scripts, or scripts imported from an existing test suite."
    },
    {
      "id": "get-automations",
      "method": "GET",
      "path": "/api/automations",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all automations for the user's active team. Supports pagination and filtering by status."
    },
    {
      "id": "get-automations-id",
      "method": "GET",
      "path": "/api/automations/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get full details of a single automation, including recorded steps, generated Playwright script, schedule, recent run history, and saved script version history . Use each entry's durable `version` value as `version_label: \"vN\"` when replaying it."
    },
    {
      "id": "patch-automations-id",
      "method": "PATCH",
      "path": "/api/automations/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update an existing automation. Only provided fields are changed. Use this to rename, update the script, change the schedule, or pause/resume."
    },
    {
      "id": "delete-automations-id",
      "method": "DELETE",
      "path": "/api/automations/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Permanently delete an automation and all its associated run history. This action cannot be undone."
    },
    {
      "id": "post-automations-generate",
      "method": "POST",
      "path": "/api/automations/generate-script",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Generate a Playwright script from recorded browser steps using AI. The generated script includes assertions, error handling, and is ready to run. Optionally saves the script to an existing automation."
    },
    {
      "id": "post-automations-optimize",
      "method": "POST",
      "path": "/api/automations/:id/optimize",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Send a Playwright script to Sonnet 4 for AI-powered optimization. Applies a 12-point checklist that automatically fixes selectors, wait strategies, assertions, error handling, auth patterns, mobile compatibility, and strict mode issues. The current script version is saved before optimization so you can undo the change."
    },
    {
      "id": "post-automations-undo",
      "method": "POST",
      "path": "/api/automations/:id/undo",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Revert the automation script to the most recent entry in `script_versions`. Pops the top of the stack (newest prior version) and promotes it to `script`. Up to 100 prior versions are retained. Versions are saved before manual edits, AI optimization, and BrowserStack rewrites. To replay (not revert to) a specific historical version without mutating current, pass its durable `version_label` to POST /automations/runs instead."
    },
    {
      "id": "post-automations-rewrite-for-bs",
      "method": "POST",
      "path": "/api/automations/:id/rewrite-for-bs",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Rewrite a script-style Python Playwright script (`sync_playwright()` + `browser.new_page()` + `if __name__ == \"__main__\":`) into pytest-style (`def test_&lt;name&gt;(page: Page):`), which is what bugAgent Live (BrowserStack) requires for Python runs. Driven by Claude Haiku; behavior is preserved - every `page.goto`, click, fill, wait, and assertion carries over. The endpoint only performs the rewrite; it doesn't save. The caller sends a subsequent `PATCH /api/automations/:id` to commit (the dashboard UI does this on Accept, tagged `version_source: \"bs_compat_rewrite\"` so the regular Undo flow can roll it back)."
    },
    {
      "id": "post-automations-runs",
      "method": "POST",
      "path": "/api/automations/runs",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Trigger an on-demand run of an automation. The run is queued and executed asynchronously. Poll `GET /api/automations/runs` or use the returned run ID to check status."
    },
    {
      "id": "get-automations-runs",
      "method": "GET",
      "path": "/api/automations/runs",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List automation runs for the user's active team. Filter by automation ID or status. Returns run results including duration, pass/fail status, and any error output. For Live (BrowserStack) runs, `video_url` points at our re-hosted copy of the BS session video (Supabase Storage, no BrowserStack login required) and `results.trace_url` points at the Playwright trace `.zip`; open the latter in `https://trace.playwright.dev/?trace=&lt;url&gt;` for a full DOM-timeline replay."
    },
    {
      "id": "post-v1-automations-run",
      "method": "POST",
      "path": "/api/v1/automations/run",
      "audience": "api_key",
      "scope": "automations:run",
      "authentication": "automations:run",
      "description": "Trigger an automation run from CI/CD pipelines. Authenticates via API key instead of session token. Designed to be called from GitHub Actions, GitLab CI, or any CI/CD system. Returns immediately with a run ID for polling."
    },
    {
      "id": "get-v1-automations-runs-id",
      "method": "GET",
      "path": "/api/v1/automations/runs/:id",
      "audience": "api_key",
      "scope": "automations:run",
      "authentication": "automations:run",
      "description": "Poll the status of a CI/CD automation run. Returns the current status, duration, and any error output. Use this to wait for completion in CI/CD scripts."
    },
    {
      "id": "post-github-connect",
      "method": "GET",
      "path": "/api/github/connect",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Initiate the GitHub OAuth flow. The endpoint stores a short-lived CSRF state cookie and redirects the browser to GitHub. Once the user authorizes, GitHub returns to the fixed bug Agent callback. Connect from Settings -> Integrations in the dashboard."
    },
    {
      "id": "get-github-repos",
      "method": "GET",
      "path": "/api/github/repos",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all GitHub repositories accessible via the connected GitHub account. Use this to select which repo to map to a bug Agent project."
    },
    {
      "id": "get-github-branches",
      "method": "GET",
      "path": "/api/github/branches",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List the branches of a repository via the connected GitHub account. Used to populate the branch dropdown when configuring OKF/OQA knowledge sync for a project."
    },
    {
      "id": "post-github-mapping",
      "method": "POST",
      "path": "/api/github/map-repo",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Map a bug Agent project to a GitHub repository. Once mapped, Playwright automation scripts created or updated in that project are automatically pushed to `tests/bugagent/&lbrace;name&rbrace;.spec.ts` in the target repo. Deleting or archiving an automation removes the file from the repo."
    },
    {
      "id": "get-github-status",
      "method": "GET",
      "path": "/api/github/status",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Check the current GitHub connection status and project-to-repo mappings for the authenticated user's workspace. Also returns recent sync history and any SHA conflict errors."
    },
    {
      "id": "delete-github-disconnect",
      "method": "POST",
      "path": "/api/github/settings",
      "audience": "dashboard_session",
      "authentication": "Manager+",
      "description": "Disconnect the GitHub integration. Removes the OAuth token and all project-to-repo mappings. Scripts already pushed to GitHub are not deleted."
    },
    {
      "id": "get-notes",
      "method": "GET",
      "path": "/api/notes",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List notes for the authenticated user's active team and project. Returns notes the user owns, project-team notes, and restricted notes that include the user. Supports full-text search, project filtering, author filtering, folder filtering, wiki filtering, tag filtering, archive filtering, sorting, and date range filtering."
    },
    {
      "id": "post-notes",
      "method": "POST",
      "path": "/api/notes",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new note. If no title is provided, the first 30 characters of the content are used as the auto-title. Notes auto-save as you type in the dashboard, but this endpoint creates the initial note record."
    },
    {
      "id": "get-notes-id",
      "method": "GET",
      "path": "/api/notes/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get full details of a single note including content and attachments. Returns the note only if the user is the author or the note has shared visibility within the same team."
    },
    {
      "id": "patch-notes-id",
      "method": "PATCH",
      "path": "/api/notes/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update an existing note. Only provided fields are changed. Only the note author can update a note. The dashboard uses this endpoint for auto-save (triggered on content changes) and manual save (Save button or Cmd/Ctrl+S)."
    },
    {
      "id": "delete-notes-id",
      "method": "DELETE",
      "path": "/api/notes/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Permanently delete a note and all its attachments. Only the note author can delete a note. This action cannot be undone."
    },
    {
      "id": "post-notes-upload",
      "method": "POST",
      "path": "/api/notes/upload",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Upload a file attachment to a note. Files are stored in Supabase Storage and linked to the note. Accepts any image, video, audio, PDF, or text/JSON file up to 400 MB per file — the same policy as bug report attachments."
    },
    {
      "id": "get-time-entries",
      "method": "GET",
      "path": "/api/time-entries",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List time tracking entries for the authenticated user's team. Supports filtering by time period, project, and category."
    },
    {
      "id": "post-time-entries",
      "method": "POST",
      "path": "/api/time-entries",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new time tracking entry. Log hours spent on QA tasks with categories for team reporting and analytics."
    },
    {
      "id": "patch-time-entries-id",
      "method": "PATCH",
      "path": "/api/time-entries",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update an existing time tracking entry. Only provided fields are changed."
    },
    {
      "id": "delete-time-entries-id",
      "method": "DELETE",
      "path": "/api/time-entries",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a time tracking entry. This action cannot be undone."
    },
    {
      "id": "get-resources",
      "method": "GET",
      "path": "/api/resources",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List the caller's CoPilot resources (recording action logs + generated Playwright scripts) in the requested workspace, optionally narrowed to a project. Returns full resource bodies so a client can mirror them locally without a follow-up request."
    },
    {
      "id": "post-resources",
      "method": "POST",
      "path": "/api/resources",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new resource for the caller. If `client_id` is provided and a row with the same `(owner_id, client_id)` already exists, the existing row is updated instead of a duplicate being inserted — this makes retries idempotent for the Chrome extension's push pipeline."
    },
    {
      "id": "get-resources-id",
      "method": "GET",
      "path": "/api/resources/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Fetch a single resource by id. Returns `404` if the resource does not exist or belongs to a different user."
    },
    {
      "id": "patch-resources-id",
      "method": "PATCH",
      "path": "/api/resources/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update whitelisted fields of a resource. Owner-only. Immutable: `owner_id`, `team_id`, `created_at`, `client_id`."
    },
    {
      "id": "delete-resources-id",
      "method": "DELETE",
      "path": "/api/resources/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a resource. Owner-only. Also removes any associated file in the `user-resources` storage bucket."
    },
    {
      "id": "post-ai-chat",
      "method": "POST",
      "path": "/api/ai/chat",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Send a message to the AI Assistant. The assistant is context-aware - it has access to your workspace's bug data, projects, Jira connection status, custom AI instructions, and uploaded knowledge documents (product specs, testing playbooks, etc.). It can answer questions about your bugs, suggest testing strategies, and guide you through creating bug reports via text or voice."
    },
    {
      "id": "post-ai-create-report",
      "method": "POST",
      "path": "/api/ai/create-report",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a report via the AI Assistant. Supports all 19 report types including bugs, feature requests, enhancements, technical debt, and more. This endpoint is typically called automatically when the assistant completes a guided report creation flow. It verifies team membership and project ownership before creating the report."
    },
    {
      "id": "post-claude-push",
      "method": "POST",
      "path": "/api/claude/push",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Generate (or regenerate) the Developer Notes for a bug report. Returns a structured analysis including probable cause, affected areas, suggested fix, verification steps, and risk assessment. Stored on the bug report and auto-regenerates on creation — this endpoint exists for manual regenerate (retry, or after the user edits the description or attachments). Uses the platform Anthropic key , so no per-team Claude connection is required."
    },
    {
      "id": "post-claude-fix-area",
      "method": "POST",
      "path": "/api/claude/fix-area",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Generate (or regenerate) the Likely Fix Area for a bug report - a narrow Sonnet output that points at the part of the codebase where the fix most likely belongs. Auto-fires on bug creation; this endpoint exists for the in-UI retry button and for external callers that want to regenerate. Uses the platform Anthropic key , so it does not require a per-team Claude connection. When the team has a `github_connections` row and the project has `github_repo` mapped, the output is grounded in the top keyword-matched files from that repo; otherwise it falls back to general guidance with a nudge to connect a repo. Writes the result back onto `bug_reports` asynchronously - the response returns 202 immediately, clients poll GET /reports/:id ."
    },
    {
      "id": "post-sessions-capture",
      "method": "POST",
      "path": "/api/sessions/capture",
      "audience": "browser_key",
      "authentication": "Browser Key",
      "description": "Submit session evidence from the browser SDK. Send a project-bound `ba_pub_` key in `X-BugAgent-Key`; the request `Origin` must exactly match one configured for that key. The workspace and project are derived from the key and cannot be selected in the body. Capture does not require a user login, but replay review and report creation do. Available on Enterprise."
    },
    {
      "id": "post-sessions-capture-video",
      "method": "POST",
      "path": "/api/sessions/capture-video",
      "audience": "browser_key",
      "authentication": "Browser Key",
      "description": "Attach one browser recording to a session previously created by the same project-bound `ba_pub_` key. Send multipart form data with the same exact allowed `Origin`. The key, workspace, project, and originating capture must all match."
    },
    {
      "id": "get-sessions",
      "method": "GET",
      "path": "/api/sessions",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List SDK sessions for the current workspace. Supports pagination and filtering."
    },
    {
      "id": "get-sessions-id",
      "method": "GET",
      "path": "/api/sessions/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get full detail of a SDK session, including all recorded events. Use this to render the session timeline or feed the events into the AI for analysis."
    },
    {
      "id": "patch-sessions",
      "method": "PATCH",
      "path": "/api/sessions",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Attach a SDK session to an existing bug report. This links the session data to the report so reviewers can see what the user did in the 60 seconds before the bug was filed."
    },
    {
      "id": "post-team-booster",
      "method": "POST",
      "path": "/api/team-booster",
      "audience": "dashboard_session",
      "authentication": "Dashboard session, Manager+",
      "description": "Instantly scale your QA team with booster testers. Accounts are provisioned automatically with tester access. Enterprise plan only. Free plan returns 403. You will not be charged until approval has been given."
    },
    {
      "id": "get-changelog",
      "method": "GET",
      "path": "/api/admin/changelog",
      "audience": "public",
      "authentication": "Public",
      "description": "List all changelog entries, most recent first. Also available as an RSS feed ."
    },
    {
      "id": "get-test-cases",
      "method": "GET",
      "path": "/api/test-cases",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "List test cases for the authenticated user's team. Supports search, filtering by priority, type, and status, sorting, and pagination."
    },
    {
      "id": "post-test-cases",
      "method": "POST",
      "path": "/api/test-cases",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Create a new test case. Two template variants: `steps` (default) — per-step Action + Expected Result grid (pass `steps`); `text` — single free-form description of the steps (pass `text_content`). Both columns can be sent in the same call; the row stores them independently so a tester switching templates later doesn't lose either side's data. Free cases may contain at most 128 KB of structured content."
    },
    {
      "id": "post-test-cases-id-attachments",
      "method": "POST",
      "path": "/api/test-cases/:id/attachments",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Upload an attachment file to a test case. Enterprise is required. Free workspaces can store URL references in the case's `urls` field instead. Multipart/form-data uses a single `file` field per request. Files are stored in the `test-case-attachments` Supabase Storage bucket; metadata is appended to the case's `attachments` jsonb column."
    },
    {
      "id": "delete-test-cases-id-attachments",
      "method": "DELETE",
      "path": "/api/test-cases/:id/attachments/:attachment_id",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Remove an attachment from a test case. Deletes the file from storage AND strips the row out of the `attachments` jsonb. Storage delete runs first; if it fails (other than \"not found\"), metadata is left in place so the user can retry."
    },
    {
      "id": "get-test-cases-id",
      "method": "GET",
      "path": "/api/test-cases/:id",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "Get a test case by ID with its full steps and execution history."
    },
    {
      "id": "patch-test-cases-id",
      "method": "PATCH",
      "path": "/api/test-cases/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Update any field of a test case. Only provided fields are changed. The resulting structured content of a Free case may not exceed 128 KB."
    },
    {
      "id": "delete-test-cases-id",
      "method": "DELETE",
      "path": "/api/test-cases/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Permanently delete a test case. Removes it from all suites."
    },
    {
      "id": "post-test-cases-duplicate",
      "method": "POST",
      "path": "/api/test-cases/duplicate",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Duplicate an existing test case including all steps, tags, and metadata. The copy is created with the name prefix \"[Copy]\"."
    },
    {
      "id": "post-test-cases-bulk",
      "method": "POST",
      "path": "/api/test-cases/bulk",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Apply one action to up to 500 test cases at once. IDs not in the caller's team are silently skipped (counted in the `skipped` response field). Used by the bulk toolbar on the Cases tab and the `bulk_update_test_cases` MCP tool."
    },
    {
      "id": "get-test-cases-id-links",
      "method": "GET",
      "path": "/api/test-cases/:id/links",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "List traceability links on a single test case - bug reports the case verifies, covers, or relates to. Established via `link_test_case_to_bug` (MCP) or the Links tab on the case detail page."
    },
    {
      "id": "get-test-cases-review-candidates",
      "method": "GET",
      "path": "/api/test-cases/review-candidates",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Informational list of test cases the system flags as archive candidates. Computed by the `find_dead_test_cases` RPC; the same flags are persisted onto `test_cases.review_flag` every Monday at 09:00 UTC by pg_cron. Drives the \"Archive candidates\" section on the Reports tab."
    },
    {
      "id": "post-test-cases-import-figma-request",
      "method": "POST",
      "path": "/api/test-cases/import/figma/request",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Step 1 of the Enterprise-only Figma zip import flow. Validates inputs, (optionally) creates a new folder, inserts a `figma_import_jobs` row with status `uploading`, and returns a signed upload URL the client PUTs the zip to directly. Max 100 MB. Uses the platform Anthropic key server-side - no per-team Claude connection is required."
    },
    {
      "id": "post-test-cases-import-figma-start",
      "method": "POST",
      "path": "/api/test-cases/import/figma/start",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Step 2 of the Figma zip import flow. Call after the zip has finished uploading to the signed URL from `/request`. Verifies the object landed in Storage, flips the job to `processing`, and signals the MCP worker to begin analysis. Returns `202 Accepted` immediately — processing runs asynchronously; poll `GET /api/test-cases/import/figma/:id` for progress."
    },
    {
      "id": "get-test-cases-import-figma-id",
      "method": "GET",
      "path": "/api/test-cases/import/figma/:id",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Poll the status of a Figma import job. Clients call this every 2 s while the modal progress bar is open. Scoped to the caller's team; returns 403 for other teams' jobs."
    },
    {
      "id": "get-test-suites",
      "method": "GET",
      "path": "/api/test-suites",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "List test suites for the authenticated user's team. Returns each suite with its case count and last run status."
    },
    {
      "id": "post-test-suites",
      "method": "POST",
      "path": "/api/test-suites",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Create a new test suite to group related test cases."
    },
    {
      "id": "get-test-suites-id",
      "method": "GET",
      "path": "/api/test-suites/:id",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "Get a test suite by ID with its full list of cases in order."
    },
    {
      "id": "patch-test-suites-id",
      "method": "PATCH",
      "path": "/api/test-suites/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Update a test suite's name or description."
    },
    {
      "id": "delete-test-suites-id",
      "method": "DELETE",
      "path": "/api/test-suites/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Delete a test suite. Test cases within the suite are not deleted."
    },
    {
      "id": "post-test-suites-id-cases",
      "method": "POST",
      "path": "/api/test-suites/:id/cases",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Add one or more test cases to a suite."
    },
    {
      "id": "delete-test-suites-id-cases",
      "method": "DELETE",
      "path": "/api/test-suites/:id/cases",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Remove a test case from a suite."
    },
    {
      "id": "patch-test-suites-id-cases",
      "method": "PATCH",
      "path": "/api/test-suites/:id/cases",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Assign or unassign a case within a suite. The assignee must be an active member of the suite's workspace and have access to its project."
    },
    {
      "id": "patch-test-suites-id-reorder",
      "method": "PATCH",
      "path": "/api/test-suites/:id/reorder",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Reorder the test cases within a suite. Provide the full ordered list of case IDs."
    },
    {
      "id": "patch-test-suites-reorder",
      "method": "PATCH",
      "path": "/api/test-suites/reorder",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Reorder sibling test suites under a shared parent (or root-level suites). Used by the drag-and-drop reorder in the Suites tab tree. All suites in the request must belong to the same parent - cross-parent reorders are rejected with HTTP 400."
    },
    {
      "id": "get-test-case-folders",
      "method": "GET",
      "path": "/api/test-case-folders",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "List folders for the authenticated team. Folders organize test cases hierarchically (one folder per case via `folder_id`) and are distinct from suites, which are many-to-many test plan groupings. Returns the full set (capped at 500) since folder trees are usually small and the entire tree is needed to render correctly."
    },
    {
      "id": "post-test-case-folders",
      "method": "POST",
      "path": "/api/test-case-folders",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Create a new folder. Pass `parent_folder_id` to nest it; folders can nest up to 3 levels deep."
    },
    {
      "id": "patch-test-case-folders-reorder",
      "method": "PATCH",
      "path": "/api/test-case-folders/reorder",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Reorder sibling folders under a shared parent (or root-level folders). Used by the drag-and-drop reorder in the Cases-tab folder sidebar. Mirrors `/api/test-suites/reorder` - same validation, same shape, against the folder table instead."
    },
    {
      "id": "get-test-case-folders-id",
      "method": "GET",
      "path": "/api/test-case-folders/:id",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "Get one project-bound folder by UUID. Resources outside the authorized workspace or project are returned as `404`."
    },
    {
      "id": "patch-test-case-folders-id",
      "method": "PATCH",
      "path": "/api/test-case-folders/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Rename, describe, or move a folder within its existing project. Supply one or more of `name`, `description`, and `parent_folder_id`. Folder depth is limited to three levels."
    },
    {
      "id": "delete-test-case-folders-id",
      "method": "DELETE",
      "path": "/api/test-case-folders/:id",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Delete a folder hierarchy. The default `mode=unfile_cases` preserves its cases and moves them to Unfiled. `mode=delete_cases` also deletes descendant cases and requires owner, admin, or manager access."
    },
    {
      "id": "get-test-case-folders-id-bulk-update",
      "method": "GET",
      "path": "/api/test-case-folders/:id/bulk-update",
      "audience": "api_key",
      "scope": "test_cases:read",
      "authentication": "test_cases:read",
      "description": "Preview a recursive folder bulk action. Returns the source folder and `total_case_count` across its descendants."
    },
    {
      "id": "post-test-case-folders-id-bulk-update",
      "method": "POST",
      "path": "/api/test-case-folders/:id/bulk-update",
      "audience": "api_key",
      "scope": "test_cases:write",
      "authentication": "test_cases:write",
      "description": "Apply one bounded recursive action to cases in a folder tree: `set_folder_color`, `set_case_color`, `add_tags`, `set_priority`, or `move_cases`. The target folder, when used, must be in the same authorized project."
    },
    {
      "id": "post-agent-test-executions",
      "method": "POST",
      "path": "/api/v1/test-executions",
      "audience": "api_key",
      "scope": "test_runs:write",
      "authentication": "test_runs:write",
      "description": "Start a new immutable suite execution or resume the matching run after a safe retry. A parent suite includes cases from every descendant suite, deduplicated into a stable order. Returns the first plan page."
    },
    {
      "id": "get-agent-test-executions-id",
      "method": "GET",
      "path": "/api/v1/test-executions/:id",
      "audience": "api_key",
      "scope": "test_runs:read",
      "authentication": "test_runs:read",
      "description": "Read canonical run state and one page of the immutable plan. The response uses the same `schema_version`, `execution`, and `plan` shape returned by the start endpoint."
    },
    {
      "id": "post-agent-test-executions-results",
      "method": "POST",
      "path": "/api/v1/test-executions/:id/results",
      "audience": "api_key",
      "scope": "test_runs:write",
      "authentication": "test_runs:write",
      "description": "Submit 1&ndash;200 results atomically. Every `case_id` must already belong to this run snapshot. Parallel batches serialize on the run, and the canonical summary is recalculated in the same database transaction."
    },
    {
      "id": "post-agent-test-executions-abort",
      "method": "POST",
      "path": "/api/v1/test-executions/:id/abort",
      "audience": "api_key",
      "scope": "test_runs:write",
      "authentication": "test_runs:write",
      "description": "Stop an interrupted execution while preserving accepted results and the current summary. The request has no body and is idempotent for an already aborted run. A completed run cannot be aborted."
    },
    {
      "id": "get-test-runs",
      "method": "GET",
      "path": "/api/test-runs",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "List test runs for the authenticated user's team. Returns each run with suite name, assignee, and pass/fail summary."
    },
    {
      "id": "post-test-runs",
      "method": "POST",
      "path": "/api/test-runs",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Create a new test run from a test suite. Snapshots all cases at creation time. If the suite has sub-suites, every descendant sub-suite's cases are included too — running a parent suite executes its whole subtree. Each case is added exactly once (parent suite wins if a case is linked to both a parent and a sub-suite) and each row in the resulting `test_run_results` records which sub-suite it came from."
    },
    {
      "id": "get-test-runs-id",
      "method": "GET",
      "path": "/api/test-runs/:id",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Get a test run by ID with all case results. Each entry in `results[]` includes `suite_id` and `case_suite_name` identifying the sub-suite the case was pulled from at run-creation time — useful for grouping results by origin on a run-detail page."
    },
    {
      "id": "patch-test-runs-id",
      "method": "PATCH",
      "path": "/api/test-runs/:id",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Update a test run's name, assignee, or status."
    },
    {
      "id": "post-test-runs-id-results",
      "method": "POST",
      "path": "/api/test-runs/:id/results",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Save the execution result for a specific test case in a run. Mark each case as passed, failed, blocked, or skipped."
    },
    {
      "id": "patch-test-runs-id-results",
      "method": "PATCH",
      "path": "/api/test-runs/:id/results",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Reassign a single test case inside an active run. Updates `test_run_results.assigned_to` for the matching `case_id`. When the new assignee is a real user and differs from the previous one, the recipient gets a bell-icon message and an email notification. Re-saving the same assignee or unassigning (`null`) is silent."
    },
    {
      "id": "post-test-runs-id-results-attachments",
      "method": "POST",
      "path": "/api/test-runs/:id/results/attachments",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Attach screenshots, screen recordings, or other evidence files to a single test case during run execution. Files are stored in the team's bug-attachments bucket and the metadata is appended to `test_run_results.attachments` (jsonb). The result row must already exist (the case has been marked passed/failed/blocked/skipped) — the endpoint returns 409 if it doesn't."
    },
    {
      "id": "delete-test-runs-id-results-attachments",
      "method": "DELETE",
      "path": "/api/test-runs/:id/results/attachments",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Remove a single attachment from a test case result. Deletes both the file in storage and the metadata entry. Returns the updated attachments list."
    },
    {
      "id": "post-test-runs-id-complete",
      "method": "POST",
      "path": "/api/test-runs/:id/complete",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Mark a test run as completed. Calculates and stores the final pass rate."
    },
    {
      "id": "post-test-runs-id-rerun-failed",
      "method": "POST",
      "path": "/api/test-runs/:id/rerun-failed",
      "audience": "dashboard_session",
      "authentication": "Dashboard session only",
      "description": "Create a new test run containing only the failed cases from a completed run. Useful for re-testing after fixes."
    },
    {
      "id": "get-test-reports",
      "method": "GET",
      "path": "/api/test-reports",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get completed test run reports with pass rate and date range filtering. Returns aggregated test execution data."
    },
    {
      "id": "get-test-reports-overview",
      "method": "GET",
      "path": "/api/test-reports/overview",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Aggregated quality KPIs + a weekly pass-rate trend for the Reports tab. Returns the current-period totals AND the prior equivalent-length period so the UI can render delta arrows."
    },
    {
      "id": "get-test-reports-failures",
      "method": "GET",
      "path": "/api/test-reports/failures",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Failure analysis for the Reports tab — four parallel \"what to fix this week?\" lists."
    },
    {
      "id": "get-test-reports-suite-health",
      "method": "GET",
      "path": "/api/test-reports/suite-health",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "One row per suite that had activity in the period, with the metrics needed to spot \"where's the rot?\" at a glance: pass rate (current + previous), trend ▲▼→, runs completed, cases executed, last run, and open bug count. Suites are sorted worst-pass-rate first so the most-broken suite is at the top. Idle suites (no runs in the current period) are excluded."
    },
    {
      "id": "get-test-reports-coverage",
      "method": "GET",
      "path": "/api/test-reports/coverage",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Catalog hygiene report. Answers \"what have I NOT been testing?\" via three coordinated views: workspace-level KPIs, a staleness distribution across six time buckets, and a per-suite coverage rollup. Idle suites and deprecated cases are excluded."
    },
    {
      "id": "get-test-reports-tester-productivity",
      "method": "GET",
      "path": "/api/test-reports/tester-productivity",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Per-tester rollup useful for capacity planning, bottleneck detection, and bug-finding effectiveness. Sorted by `cases_executed` descending. A user shows up if either they appear as `test_run_results.tester_id` in the period or they were assigned a run created in the period (so a tester with zero executions but assigned work still surfaces — that's the bottleneck signal)."
    },
    {
      "id": "get-test-reports-export-pdf",
      "method": "GET",
      "path": "/api/test-reports/export.pdf",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "One-click PDF export of the QA Reports dashboard, suitable for stakeholder distribution. Renders a 3-page brand-styled report covering the most important sections: at-a-glance KPIs & trend, what-to-fix (failing / flaky / regressed cases), and per-suite + per-tester rollups."
    },
    {
      "id": "get-export-okf",
      "method": "GET",
      "path": "/api/export/okf",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Export a project's QA knowledge - bug reports, test cases, automations, and performance, security, and exploratory tests - as an OKF/OQA markdown bundle (the Open Query Agent format used by oqa.ai )."
    },
    {
      "id": "post-export-okf-sync",
      "method": "POST",
      "path": "/api/export/okf-sync",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Save the per-project GitHub knowledge-sync target - the repo, branch, and path that a project's OKF/OQA bundle is committed to - and, unless `run` is `false`, immediately commit the project's current OKF/OQA bundle (bug reports, test cases, automations, and performance, security, and exploratory tests, in the oqa.ai format) to that repo via the GitHub Contents API."
    },
    {
      "id": "get-geo-snap",
      "method": "GET",
      "path": "/api/geo-snap",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List saved Geo-Snap screenshots. Filter by country, search by URL, and paginate results."
    },
    {
      "id": "post-geo-snap",
      "method": "POST",
      "path": "/api/geo-snap",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Capture screenshots of a URL from one or more countries. Free plan: 1 country per request, 10 saved screenshots. Enterprise: up to 5 countries per request, unlimited saved screenshots."
    },
    {
      "id": "delete-geo-snap-id",
      "method": "DELETE",
      "path": "/api/geo-snap/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a saved Geo-Snap screenshot by ID."
    },
    {
      "id": "get-mobile-apps",
      "method": "GET",
      "path": "/api/mobile/apps",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List uploaded mobile apps. Filter by platform, search by name, and paginate results."
    },
    {
      "id": "post-mobile-apps",
      "method": "POST",
      "path": "/api/mobile/apps",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Upload an APK (Android) or IPA (iOS) app binary. Uses multipart/form-data. Max file size: 500 MB."
    },
    {
      "id": "get-mobile-apps-id",
      "method": "GET",
      "path": "/api/mobile/apps/:id",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Get full details for a mobile app, including automation count."
    },
    {
      "id": "patch-mobile-apps-id",
      "method": "PATCH",
      "path": "/api/mobile/apps/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Update an uploaded mobile app's metadata. Changing its package or simulator target requires authority over every login profile used by linked automations, or an active workspace owner/admin role. The same authorization applies when replacing the app binary through the dashboard."
    },
    {
      "id": "put-mobile-apps-id",
      "method": "PUT",
      "path": "/api/mobile/apps/:id",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Replace an uploaded app binary after the dashboard has completed its signed upload. Supply `storage_path`, and optionally `file_size` and `version`. This storage workflow requires an interactive dashboard session and is not an API-key upload contract."
    },
    {
      "id": "delete-mobile-apps-id",
      "method": "DELETE",
      "path": "/api/mobile/apps/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Delete an uploaded mobile app and its storage file. Automations referencing this app will be orphaned."
    },
    {
      "id": "get-mobile-automations",
      "method": "GET",
      "path": "/api/mobile/automations",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List mobile automation scripts. Filter by app, script type, and status."
    },
    {
      "id": "post-mobile-automations",
      "method": "POST",
      "path": "/api/mobile/automations",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create a mobile automation. Existing action recordings and imported scripts use `appium_actions`. Recorder-produced action lists are capped at 500 events and must include a resolved, versioned selector decision for every tap; ambiguous taps and obfuscated Android resource-ID placeholders return `422` until the user selects a stable locator or explicitly confirms the recorded coordinate. To run one known-working Maestro YAML flow directly on BrowserStack against an APK or IPA, set `script_type` to `maestro` and `execution_mode` to `browserstack_maestro`. Its `appId` must exactly match the linked app's package or bundle ID; the first validated native flow establishes that ID when the app record does not have one yet."
    },
    {
      "id": "post-mobile-automations-import",
      "method": "POST",
      "path": "/api/mobile/automations/import",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create a runnable mobile automation from a pasted test script, preserving the developer's real locators. Parses Appium (Python or JS), WebdriverIO, Maestro, and Playwright (mobile-web) and stores the parsed steps as `recorded_actions` that run verbatim (no agent loop). Obfuscated Android resource-ID placeholders are skipped and returned as selector-mapping warnings. Android only. Enterprise plan."
    },
    {
      "id": "get-mobile-credentials",
      "method": "GET",
      "path": "/api/mobile/credentials",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List reusable login profiles for Mobile, Web Automation, and Exploratory AI - non-secret fields only . Stored username/password values are AES-encrypted and never returned. Private profiles owned by another user and profiles in inaccessible projects are omitted; shared profiles are available only to active members with access to the same project."
    },
    {
      "id": "post-mobile-credentials",
      "method": "POST",
      "path": "/api/mobile/credentials",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create a reusable login profile. The username and password are AES-encrypted at rest immediately and can never be read back. Private profiles are creator-only. Shared profiles may be selected by active members with access to the same project; the creator or an active workspace owner/admin may rename, rotate, or delete them. Only the creator can change visibility. Enterprise plan only. Profile names are unique per project."
    },
    {
      "id": "patch-mobile-credentials-id",
      "method": "PATCH",
      "path": "/api/mobile/credentials/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Rotate, rename, or change the visibility of a login profile. Include only the fields you want to change. Supplied secrets are re-encrypted immediately. The active creator may update any profile field. An active workspace owner/admin may rename or rotate a shared profile but cannot change its visibility; private profiles remain creator-only. Enterprise plan only."
    },
    {
      "id": "delete-mobile-credentials-id",
      "method": "DELETE",
      "path": "/api/mobile/credentials/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Soft-delete a login profile. The active creator may delete any profile they created; an active workspace owner/admin may delete a shared profile. Private profiles remain creator-only. The profile row and historical run references remain for audit; future-use defaults are cleared so scheduled runs cannot silently use a deleted secret. Enterprise plan only."
    },
    {
      "id": "get-mobile-variable-profiles",
      "method": "GET",
      "path": "/api/mobile/variable-profiles",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List readable non-secret variable profiles for one authorized project. Requires `mobile:read`. Values are reusable synthetic Maestro inputs, not credentials: authorized project members receive the complete values and private visual evidence may display values rendered by the app."
    },
    {
      "id": "post-mobile-variable-profiles",
      "method": "POST",
      "path": "/api/mobile/variable-profiles",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create a project-scoped non-secret data profile. Requires `mobile:write`. Keys must match `DATA_[A-Z][A-Z0-9_]*` within the documented 64-character format. Credential and Maestro-runtime names are reserved. Profiles contain 1&ndash;100 string values, each at most 4096 UTF-8 bytes, and at most 65536 bytes total."
    },
    {
      "id": "get-mobile-variable-profiles-id",
      "method": "GET",
      "path": "/api/mobile/variable-profiles/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Read one non-secret variable profile and its values. Requires `mobile:read`, active workspace membership, and project access. Inaccessible and cross-tenant IDs return `404`."
    },
    {
      "id": "patch-mobile-variable-profiles-id",
      "method": "PATCH",
      "path": "/api/mobile/variable-profiles/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Rename a profile or replace its complete values object. Requires `mobile:write`. Only the active profile creator or an active workspace owner/admin may update it; project managers do not receive profile-use authority automatically."
    },
    {
      "id": "delete-mobile-variable-profiles-id",
      "method": "DELETE",
      "path": "/api/mobile/variable-profiles/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Soft-delete a profile. Requires `mobile:write` and active creator or workspace owner/admin authority. Historical run references remain for audit; automation defaults are cleared, so future immediate runs and schedules no longer inherit the deleted profile."
    },
    {
      "id": "get-mobile-automations-id",
      "method": "GET",
      "path": "/api/mobile/automations/:id",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Get full details for a mobile automation, including app info and recent runs."
    },
    {
      "id": "patch-mobile-automations-id",
      "method": "PATCH",
      "path": "/api/mobile/automations/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Update a mobile automation script or its metadata. The execution mode is immutable. Updates to native Maestro scripts are revalidated as a single YAML flow; recorded actions remain rejected for native Maestro mode. Action-replay updates are capped at 500 events and reject unresolved recorder taps with `422`. Same-project `credential_id` and non-secret `variable_profile_id` defaults may be set or cleared when the YAML references their supported placeholders."
    },
    {
      "id": "delete-mobile-automations-id",
      "method": "DELETE",
      "path": "/api/mobile/automations/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Delete a mobile automation. Fails if the automation has active schedules - delete schedules first."
    },
    {
      "id": "get-mobile-automations-id-versions",
      "method": "GET",
      "path": "/api/mobile/automations/:id/versions",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List up to 100 retained script versions for an authorized mobile automation. Sensitive input values are not returned."
    },
    {
      "id": "post-mobile-automations-id-versions",
      "method": "POST",
      "path": "/api/mobile/automations/:id/versions",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Restore a retained version while atomically preserving the current script as a new history entry."
    },
    {
      "id": "get-mobile-automations-id-visual-baseline",
      "method": "GET",
      "path": "/api/mobile/automations/:id/visual-baseline",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Read baseline metadata. Add `?item=N` to receive a short-lived redirect to one authorized baseline image."
    },
    {
      "id": "post-mobile-automations-id-visual-baseline",
      "method": "POST",
      "path": "/api/mobile/automations/:id/visual-baseline",
      "audience": "dashboard_session",
      "authentication": "Dashboard session",
      "description": "Create a visual baseline from a passed run using JSON `{`{ \"run_id\": \"uuid\" }`}`, or upload one image as multipart form data in `file`. Uploads are limited to 10 MB. This evidence-management write currently requires an interactive dashboard session."
    },
    {
      "id": "delete-mobile-automations-id-visual-baseline",
      "method": "DELETE",
      "path": "/api/mobile/automations/:id/visual-baseline",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Remove the saved baseline and disable visual comparison for the automation."
    },
    {
      "id": "get-mobile-runs",
      "method": "GET",
      "path": "/api/mobile/runs",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List mobile test runs. Filter by automation and status."
    },
    {
      "id": "post-mobile-runs",
      "method": "POST",
      "path": "/api/mobile/runs",
      "audience": "api_key",
      "scope": "mobile:run",
      "authentication": "mobile:run",
      "description": "Trigger a mobile test run on BrowserStack. `appium_actions` automations use the existing Appium replay executor. `browserstack_maestro` automations upload and execute the stored YAML through BrowserStack's native Maestro API for both Android and iOS."
    },
    {
      "id": "get-mobile-runs-id",
      "method": "GET",
      "path": "/api/mobile/runs/:id",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Get results for an authorized mobile test run. Uncredentialed Appium runs include Appium/device/network logs; uncredentialed native Maestro runs include BrowserStack video, a representative end-of-run frame, any YAML `takeScreenshot` output, the flow result, Maestro command logs, device/network logs, and duration. Credentialed native BrowserStack Maestro runs retain private visual evidence, filtered logs, real step names, detailed failures, status, duration, counts, and BrowserStack identifiers for 30 days. Exact known credential values and recognized generic secret patterns are removed before text is persisted. Because this filtering cannot guarantee identification of every possible secret displayed by the tested app, detailed text is withheld whenever credential decryption or redaction context is unavailable or sanitization cannot be proven safe; status and available visual evidence remain. Workspace membership and project access are enforced for diagnostics. Private media is delivered with five-minute signed links. Auto-created bugs and Slack/email notifications remain generic and do not copy credentialed diagnostics. Credentialed Appium action runs return structural metadata and BrowserStack identifiers only."
    },
    {
      "id": "patch-mobile-runs-id",
      "method": "PATCH",
      "path": "/api/mobile/runs/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Archive a completed mobile test run. Archived runs are hidden from the default listing but can be viewed with the `status=archived` filter."
    },
    {
      "id": "post-mobile-runs-id-bug-report",
      "method": "POST",
      "path": "/api/mobile/runs/:id/bug-report",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create or link a bug report for an authorized mobile run whose status is `failed` or `error`. This idempotent operation requires `mobile:write`. Repeated requests return the report already linked to the run. Credentialed runs produce a generic report without private logs, screenshots, or video; authorized users can review that evidence on the run detail page."
    },
    {
      "id": "get-mobile-runs-id-artifact",
      "method": "GET",
      "path": "/api/mobile/runs/:id/artifact",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Return a five-minute signed redirect for an artifact already referenced by the authorized run. Use the exact `name` from a run response, such as `video.mp4` or `screenshot-1.jpg`. Arbitrary storage paths are rejected."
    },
    {
      "id": "get-mobile-devices",
      "method": "GET",
      "path": "/api/mobile/devices",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List available BrowserStack real devices for mobile testing. Optionally filter by platform."
    },
    {
      "id": "post-mobile-refinements",
      "method": "POST",
      "path": "/api/mobile/refinements",
      "audience": "api_key",
      "scope": "mobile:run",
      "authentication": "mobile:run",
      "description": "Start a Refine with AI job for a recorded mobile automation, or resume its active one. An agent iteratively hardens the recorded tour into a self-healing, device-certified script - layering in resilient locators (accessibility id, resource id, semantic labels) and proving it with repeated green runs on a real device. One active refinement per automation; jobs count against a monthly plan quota. Android apps only for now. Enabled per workspace - a workspace without Refine with AI access receives `403` (contact us to turn it on)."
    },
    {
      "id": "get-mobile-refinements-id",
      "method": "GET",
      "path": "/api/mobile/refinements/:id",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Get a refinement's state plus its event log (transcript, stage changes, run reports). Events are ordered by a monotonic cursor for replay."
    },
    {
      "id": "post-mobile-refinements-id-messages",
      "method": "POST",
      "path": "/api/mobile/refinements/:id/messages",
      "audience": "api_key",
      "scope": "mobile:run",
      "authentication": "mobile:run",
      "description": "Send a chat message to the refining agent. Messages queue and are delivered at the next safe point in the loop - you can send anytime while the job is active. Secrets in the message (API keys, tokens, private keys, `password=`-style assignments) are automatically masked before the message is stored or reaches the agent."
    },
    {
      "id": "post-mobile-refinements-id-interrupt",
      "method": "POST",
      "path": "/api/mobile/refinements/:id/interrupt",
      "audience": "api_key",
      "scope": "mobile:run",
      "authentication": "mobile:run",
      "description": "Stop an active refinement. The job pauses (keeping its draft and history) and any held device session is released. Resume later via `POST /api/mobile/refinements`."
    },
    {
      "id": "post-mobile-refinements-id-approve",
      "method": "POST",
      "path": "/api/mobile/refinements/:id/approve",
      "audience": "api_key",
      "scope": "mobile:run",
      "authentication": "mobile:run",
      "description": "Approve the refined draft: freezes it as an immutable script version (with full provenance) and copies it into the automation's `recorded_actions` so future runs use it."
    },
    {
      "id": "get-automation-schedules",
      "method": "GET",
      "path": "/api/automations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all web automation schedules for the team. Returns cron expression, timezone, device, notification settings, and next run time."
    },
    {
      "id": "post-automation-schedules",
      "method": "POST",
      "path": "/api/automations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a web automation schedule. Requires `automation_id` and `cron_expression`. Optional: `timezone`, `device` (desktop, iphone-15, galaxy-s23, etc. - Virtual mode viewport emulation), `login_profile_id`, `notify_on_fail` (none/email/slack/both), `notify_email`, `slack_channel_id`. A profile UUID selects an authorized project-scoped identity for this schedule; `null` explicitly runs without a profile and does not inherit later automation-default changes. If the automation uses reusable authenticated sessions, each selected profile maintains a separately encrypted, target- and execution-scoped session. Supports BrowserStack Live via `browserstack: true` + `bs_browser`, `bs_os`, `bs_os_version` - same matrix as `POST /automations/runs`: Node scripts get desktop + real Android + real iPhone; Python scripts get desktop only. See the Live-browser device list on that endpoint for device names."
    },
    {
      "id": "get-mobile-schedules",
      "method": "GET",
      "path": "/api/mobile/schedules",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "List scheduled mobile test runs with cron expression, timezone, and notification settings."
    },
    {
      "id": "post-mobile-schedules",
      "method": "POST",
      "path": "/api/mobile/schedules",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Create a scheduled mobile test run. Runs are dispatched to the configured devices on the cron schedule and inherit the automation's current login-profile and non-secret variable-profile defaults. Private login profiles require their active creator; shared login profiles require active same-project access. Non-secret variable profiles retain their creator-or-owner/admin policy. Change the automation default to change future scheduled inputs; schedules do not store a separate variable-profile override."
    },
    {
      "id": "get-mobile-schedules-id",
      "method": "GET",
      "path": "/api/mobile/schedules/:id",
      "audience": "api_key",
      "scope": "mobile:read",
      "authentication": "mobile:read",
      "description": "Get one mobile schedule, including its automation name, cron expression, timezone, devices, notification settings, enabled state, and next run time. The schedule must belong to a workspace the caller can access."
    },
    {
      "id": "patch-mobile-schedules-id",
      "method": "PATCH",
      "path": "/api/mobile/schedules/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Update a mobile test schedule. Only the active schedule creator or an active workspace owner/admin may change its timing, devices, notification settings, or enabled state."
    },
    {
      "id": "delete-mobile-schedules-id",
      "method": "DELETE",
      "path": "/api/mobile/schedules/:id",
      "audience": "api_key",
      "scope": "mobile:write",
      "authentication": "mobile:write",
      "description": "Delete a mobile test schedule. Only the active schedule creator or an active workspace owner/admin may delete it."
    },
    {
      "id": "get-performance-tests",
      "method": "GET",
      "path": "/api/performance/tests",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all performance test configurations for the current team."
    },
    {
      "id": "post-performance-tests",
      "method": "POST",
      "path": "/api/performance/tests",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new performance test configuration. Combines page quality audits with distributed load testing. Supports mobile app profiling on real Android/iOS devices via BrowserStack. Enterprise only."
    },
    {
      "id": "get-performance-tests-id",
      "method": "GET",
      "path": "/api/performance/tests/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get a single performance test configuration by ID."
    },
    {
      "id": "patch-performance-tests-id",
      "method": "PATCH",
      "path": "/api/performance/tests/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update a performance test configuration. Only include fields you want to change."
    },
    {
      "id": "delete-performance-tests-id",
      "method": "DELETE",
      "path": "/api/performance/tests/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a performance test configuration and all its associated runs."
    },
    {
      "id": "post-performance-run",
      "method": "POST",
      "path": "/api/performance/run",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Trigger a performance test run (page audit + load test). The run executes asynchronously; poll `GET /performance/runs/:id` for results. Auto-creates a bug report when the performance score drops below the configured threshold."
    },
    {
      "id": "get-performance-runs-id",
      "method": "GET",
      "path": "/api/performance/runs/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get full results for a performance test run. Includes Lighthouse scores (Performance, Accessibility, Best Practices, SEO), Core Web Vitals (LCP, FID, CLS, FCP, TTFB, INP, TBT, SI), and load test metrics (VUs, total requests, RPS, p50/p90/p95/p99 latencies)."
    },
    {
      "id": "get-performance-runs-id-pdf",
      "method": "GET",
      "path": "/api/performance/runs/:id/pdf",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Download a PDF report for a performance run. Includes Lighthouse scores, Core Web Vitals, load test summary with latency percentiles, and test metadata."
    },
    {
      "id": "post-performance-runs-id-archive",
      "method": "POST",
      "path": "/api/performance/runs/:id/archive",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Archive a completed performance test run. Archived runs are hidden from the default listing but can be viewed with the `status=archived` filter on `GET /performance/runs`."
    },
    {
      "id": "delete-performance-runs-id-archive",
      "method": "DELETE",
      "path": "/api/performance/runs/:id/archive",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Unarchive a previously archived performance test run, restoring it to the default listing."
    },
    {
      "id": "get-performance-usage",
      "method": "GET",
      "path": "/api/performance/usage",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Check monthly performance test usage against plan limits. Performance testing is Enterprise-only. Customer limits: Free=0, Enterprise=unlimited."
    },
    {
      "id": "post-security-scans",
      "method": "POST",
      "path": "/api/security/scans",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a security scan configuration. Requires an Enterprise plan."
    },
    {
      "id": "post-security-run",
      "method": "POST",
      "path": "/api/security/run",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Trigger a security scan. Web scans require a verified domain. Mobile scans require an uploaded app with a valid file URL. Counts against monthly quota."
    },
    {
      "id": "get-security-runs",
      "method": "GET",
      "path": "/api/security/runs/{'{id}'}",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get scan run status and results. Poll this endpoint while status is `queued` or `running`."
    },
    {
      "id": "get-security-domains",
      "method": "GET",
      "path": "/api/security/domains",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List verified domains for the team. Use POST to add a domain and receive a DNS TXT verification token."
    },
    {
      "id": "get-security-schedules",
      "method": "GET",
      "path": "/api/security/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List scheduled security scans for the team. Filter by `scan_id` query param to get schedules for a specific scan config. Each schedule has one and only one parent scan config."
    },
    {
      "id": "post-security-schedules",
      "method": "POST",
      "path": "/api/security/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a scheduled security scan. One schedule per scan config. When the schedule fires, the scan uses the `scan_depth` configured on the scan itself. Every run counts against the Enterprise monthly security scan cap of 20. Admin users bypass the cap."
    },
    {
      "id": "patch-security-schedules-id",
      "method": "PATCH",
      "path": "/api/security/schedules/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update a security scan schedule. Updating `cron_expression`, `timezone`, or re-enabling automatically recomputes `next_run_at`."
    },
    {
      "id": "delete-security-schedules-id",
      "method": "DELETE",
      "path": "/api/security/schedules/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete a security scan schedule. Does not affect the parent scan config or any completed runs."
    },
    {
      "id": "get-code-review-prs",
      "method": "GET",
      "path": "/api/code-review/prs?repo=owner/repo",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List open pull requests for a GitHub repository. Returns PR number, title, author, head branch, and existing review status if previously reviewed. Requires a GitHub integration configured in Settings."
    },
    {
      "id": "post-code-review-review",
      "method": "POST",
      "path": "/api/code-review/review",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Trigger an AI code review on a pull request. Fetches the PR diff from GitHub and sends it to Claude for analysis. Returns the review ID with quality score and findings. Enterprise only."
    },
    {
      "id": "get-code-review-reviews",
      "method": "GET",
      "path": "/api/code-review/reviews",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List past code reviews for the team. Returns review ID, repo, PR number/title, quality score, severity counts, and timestamps. Ordered by most recent."
    },
    {
      "id": "get-code-review-reviews-id",
      "method": "GET",
      "path": "/api/code-review/reviews/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get a single code review with all findings. Each finding includes severity (critical/high/medium/low), category (bug/security/performance/style/logic/maintainability), title, description, suggestion, file path, line numbers, and code snippet."
    },
    {
      "id": "get-code-review-settings",
      "method": "GET",
      "path": "/api/code-review/settings",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get code review settings for the team. Returns custom instructions, auto-review toggle, post-comments toggle, auto-bug creation config, and enabled status."
    },
    {
      "id": "post-code-review-settings",
      "method": "POST",
      "path": "/api/code-review/settings",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update code review settings. All fields optional - only provided fields are updated."
    },
    {
      "id": "get-code-review-usage",
      "method": "GET",
      "path": "/api/code-review/usage",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Check code review usage. Returns reviews used and plan info. AI code review is Enterprise-only; unlimited on Enterprise."
    },
    {
      "id": "get-code-review-analytics",
      "method": "GET",
      "path": "/api/code-review/analytics",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get code review analytics for the team. Returns trends (daily review counts + avg scores), finding category and source breakdowns, severity distribution, velocity metrics (reviews/week, avg time, avg findings), top repos, top PR authors, and recent quality score sparkline data. Query param: `days` (7, 30, or 90, default 30)."
    },
    {
      "id": "get-github-webhooks",
      "method": "GET",
      "path": "/api/github/webhooks",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all GitHub webhooks registered for the team. Returns webhook ID, repo, active status, and creation date."
    },
    {
      "id": "post-github-webhooks",
      "method": "POST",
      "path": "/api/github/webhooks",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Register a GitHub webhook on a repository for auto-review. Requires `repo` (e.g. `owner/name`). Creates a webhook on GitHub that sends `pull_request` events to bugAgent. The webhook secret is auto-generated and stored securely."
    },
    {
      "id": "delete-github-webhooks",
      "method": "DELETE",
      "path": "/api/github/webhooks",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Remove a GitHub webhook from a repository. Requires `repo`. Deletes the webhook from both GitHub and the bugAgent database."
    },
    {
      "id": "post-github-webhook",
      "method": "POST",
      "path": "/api/github/webhook",
      "audience": "public",
      "authentication": "Public (Webhook)",
      "description": "GitHub webhook receiver endpoint. Handles `pull_request` events. Verifies HMAC-SHA256 signature, checks auto-review settings, and triggers a Claude-powered code review. Only processes `opened` and `synchronize` actions. Deduplicates by commit SHA."
    },
    {
      "id": "get-explorations",
      "method": "GET",
      "path": "/api/explorations",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List all Exploratory AI configs for the team. Returns name, target URL, status, agent count, selected strategies, last run info, and run count."
    },
    {
      "id": "post-explorations",
      "method": "POST",
      "path": "/api/explorations",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create a new exploration config. Requires `name` and `target_url`. Optional: `exploration_context` (JSONB with focus_areas, instructions, auth_config containing login_url and username, exclude_paths, max_depth, test_data), write-only `auth_password`, `project_id`, `auto_create_bug`, `min_severity_for_bug`, `agent_count` (1&ndash;10, clamped to plan limit; Enterprise: max 10), `agent_strategies` (array of strategy IDs: happy_path, edge_case, security, accessibility, error_path, performance, mobile, data_integrity, navigation, custom). Saved passwords are encrypted and are never returned by REST or MCP responses."
    },
    {
      "id": "post-explorations-run",
      "method": "POST",
      "path": "/api/explorations/run",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Trigger an exploration run. Requires `exploration_id`. Dispatches to the runner for 5-phase execution (Recon, Plan, Execute, Analyze, Report). When `agent_count &gt; 1`, multiple agents run Plan/Execute/Analyze in parallel after a shared Recon phase. Returns the run ID. Poll `GET /api/explorations/runs/:id` for status and per-agent progress via `agent_progress`."
    },
    {
      "id": "get-explorations-runs-id",
      "method": "GET",
      "path": "/api/explorations/runs/:id",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get exploration run details including phase data (recon, plan, execute, analyze), findings with agent attribution (`agent_index`, `agent_strategy`), per-agent progress (`agent_progress`), and linked bug reports. Use for polling during execution or viewing results."
    },
    {
      "id": "get-explorations-usage",
      "method": "GET",
      "path": "/api/explorations/usage",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Check monthly Exploratory AI usage. Exploratory AI is Enterprise-only; Enterprise: unlimited."
    },
    {
      "id": "get-explorations-trends",
      "method": "GET",
      "path": "/api/explorations/trends",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Get exploration history and trends. Returns run history, finding trends by type and severity over time, screenshot comparisons between consecutive runs, and delta vs previous run. Query params: `exploration_id` (optional filter), `days` (7/30/90, default 30)."
    },
    {
      "id": "get-explorations-schedules",
      "method": "GET",
      "path": "/api/explorations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "List exploration schedules for the signed-in workspace."
    },
    {
      "id": "post-explorations-schedules",
      "method": "POST",
      "path": "/api/explorations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Create an exploration schedule. Requires `exploration_id` and `cron_expression`, with optional email or Slack failure notification settings."
    },
    {
      "id": "patch-explorations-schedules",
      "method": "PATCH",
      "path": "/api/explorations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Update an authorized exploration schedule, including its enabled state, cron expression, timezone, and notification settings."
    },
    {
      "id": "delete-explorations-schedules",
      "method": "DELETE",
      "path": "/api/explorations/schedules",
      "audience": "dashboard_session",
      "authentication": "Auth Required",
      "description": "Delete an authorized exploration schedule."
    }
  ]
}
