Skip to main content
Getting Started

Welcome to the bugAgent API

The bugAgent REST API gives you full programmatic access to bug reports, feature requests, enhancements, projects, API keys, user profiles, and more. Everything available in the dashboard and MCP tools is accessible via the API.

💡
MCP + REST — bugAgent also supports the Model Context Protocol for AI agent integrations. The REST API and MCP tools share the same feature set.

Introduction

The bugAgent API is organized around REST. It accepts JSON-encoded request bodies, returns JSON responses, and uses standard HTTP methods and status codes.

All API requests must include a Content-Type: application/json header for requests with a body. Responses always return Content-Type: application/json.

🔐

Secure

All requests over HTTPS. API keys are SHA-256 hashed at rest.

Fast

Sub-100ms responses for most endpoints. Built on edge infrastructure.

🤖

Agent-Friendly

Designed for both human and AI agent consumption. Consistent JSON schemas.

Base URL

All API endpoints are relative to the base URL:

Base URL
https://app.bugagent.com/api

For example, to list bug reports:

Example
GET https://app.bugagent.com/api/reports

Authentication

The API supports two authentication methods:

API Key (recommended)

Generate an API key from the dashboard settings or via the POST /api/keys endpoint. Include it as a Bearer token:

cURL
curl https://app.bugagent.com/api/reports \
  -H "Authorization: Bearer ba_live_your_key_here"
⚠️
Keep your API key secret. Do not expose it in client-side code, public repos, or browser requests. API keys have full access to your account.

Session Cookie

If you're authenticated via the dashboard (browser session), API requests from the same origin automatically use your session cookie. This is used by the dashboard frontend.

Connect via MCP

Instead of calling the REST API directly, you can connect to bugAgent through the Model Context Protocol (MCP) server at https://mcp.bugagent.com/mcp. The MCP server exposes 60+ tools (bug reports, projects, automations, explorations, security scans, etc.) to any MCP-compatible client — Claude Desktop, Claude Code, Cursor, VS Code, Claude.ai (web), or the Inspector. Most clients authenticate with the same ba_live_ API key you generated above. For OAuth-aware hosts that require static client_id + client_secret upfront (Claude.ai’s web Connectors form is the most common example), generate platform-agnostic OAuth credentials from Settings → Developers → MCP Connectors. Those credentials work for any MCP host that speaks OAuth 2.0 Authorization Code with PKCE.

Seven ways to connect (macOS + Windows)

1. MCP Inspector (web UI, recommended for first-time testing)

Terminal / PowerShell
npx @modelcontextprotocol/inspector

When the browser opens: Transport Streamable HTTP, URL https://mcp.bugagent.com/mcp, Connection Type Proxy. Open the Authentication tab and add a header with Name Authorization and Value Bearer ba_live_YOUR_KEY. Click Connect.

2. Claude Desktop (Mac + Windows)

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (Mac) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

claude_desktop_config.json
{
  "mcpServers": {
    "bugagent": {
      "type": "http",
      "url": "https://mcp.bugagent.com/mcp",
      "headers": {
        "Authorization": "Bearer ba_live_YOUR_KEY_HERE"
      }
    }
  }
}

Fully quit and relaunch Claude Desktop. Tools hammer icon will show bugAgent.

3. Claude Code (CLI)

Terminal / PowerShell
claude mcp add --transport http bugagent https://mcp.bugagent.com/mcp \
  --header "Authorization: Bearer ba_live_YOUR_KEY_HERE"

Restart your session. Verify with claude mcp list.

4. Cursor (Mac + Windows)

Settings → MCP → + Add new MCP server. Select HTTP transport, URL https://mcp.bugagent.com/mcp, header Authorization: Bearer ba_live_YOUR_KEY, Save. Or edit ~/.cursor/mcp.json directly with the same JSON block as Claude Desktop.

5. VS Code with Continue extension (Mac + Windows)

Install the Continue extension, then edit ~/.continue/config.json (Mac) or %USERPROFILE%\.continue\config.json (Windows):

~/.continue/config.json
{
  "mcpServers": [
    {
      "name": "bugagent",
      "type": "streamable-http",
      "url": "https://mcp.bugagent.com/mcp",
      "requestOptions": {
        "headers": {
          "Authorization": "Bearer ba_live_YOUR_KEY_HERE"
        }
      }
    }
  ]
}

6. OAuth-aware MCP hosts (Claude.ai web shown as the example)

Hosts that require static client_id + client_secret upfront use bugAgent’s OAuth credentials. The credentials are MCP-host-agnostic — any OAuth client supporting Authorization Code + PKCE can use them. The walkthrough below uses the Claude.ai web app as the most common example.

  1. In bugAgent: Settings → Developers → MCP ConnectorsGenerate connector. Choose Confidential mode. Paste the redirect URI your MCP host requires — for the Claude.ai web app that’s https://claude.ai/api/mcp/auth_callback; other hosts will document their own callback URL. Copy the client_id and client_secret shown once on the success screen.
  2. In your MCP host’s connector / OAuth settings, paste:
    • Server URL: https://mcp.bugagent.com/mcp
    • Client ID + Client Secret: from step 1
    • Authorization URL: https://mcp.bugagent.com/authorize
    • Token URL: https://mcp.bugagent.com/token
    For Claude.ai specifically: claude.ai/customize/connectorsAdd MCP connector.
  3. Save. The host redirects you to bugAgent to sign in (Google or email/password) and approve consent, then completes the OAuth handshake.

Revoke any time from the same Settings page — takes effect on the next request.

7. curl / Terminal (JSON-RPC 2.0)

MCP Streamable HTTP speaks JSON-RPC 2.0. The Accept: application/json, text/event-stream header is required.

macOS / Linux Terminal
curl -N -s https://mcp.bugagent.com/mcp \
  -H "Authorization: Bearer ba_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# Call a tool
curl -N -s https://mcp.bugagent.com/mcp \
  -H "Authorization: Bearer ba_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc":"2.0",
    "id":2,
    "method":"tools/call",
    "params":{"name":"list_bug_reports","arguments":{"limit":5}}
  }'
Windows PowerShell
$headers = @{
  "Authorization" = "Bearer ba_live_YOUR_KEY_HERE"
  "Content-Type" = "application/json"
  "Accept" = "application/json, text/event-stream"
}
$body = '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Invoke-RestMethod -Uri "https://mcp.bugagent.com/mcp" `
  -Method Post -Headers $headers -Body $body
📘
See the MCP documentation → Connect to the Server for full step-by-step walkthroughs with screenshots and troubleshooting for each of the six connection options.

Error Handling

The API uses standard HTTP status codes and returns errors as JSON:

Error Response
{
  "error": "Description of what went wrong"
}
CodeMeaning
200Success
201Created
400Bad request — invalid or missing parameters
401Unauthorized — missing or invalid authentication
403Forbidden — insufficient permissions
404Not found — resource doesn't exist or you don't have access
409Conflict — resource already exists
413Payload too large
422Unprocessable — validation failed
500Internal server error

Available SDKs

Official SDKs are coming soon for popular languages:

🟢

Node.js / TypeScript

Coming soon

🐍

Python

Coming soon

🦀

Rust

Coming soon

Go

Coming soon

In the meantime, you can use any HTTP client to interact with the API, or connect via the MCP server for AI agent workflows.

Contribute

We welcome contributions to our SDKs. If you have any suggestions or improvements, please feel free to open a pull request!

If you have any other languages you would like us to support, please reach out to us at [email protected].

License

All of our SDKs are open source and available under the MIT License. You are free to use, modify, and distribute them in your own projects.

API Endpoints
POST /api/auth/register Public

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.

⚠️
Rate limited: 5 attempts per 15 minutes per email address.

Request Body

email required
string — Valid email address for the new account
password required
string — Password (8-128 characters)
full_name optional
string — Display name (max 200 characters)

Response

201 Created
{
  "userId": "uuid",
  "email": "[email protected]"
}
POST /api/auth/login Public

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.

⚠️
Rate limited: 5 attempts per 15 minutes per email address.

Request Body

email required
string — Account email
password required
string — Account password

Response

200 OK
{
  "userId": "uuid",
  "email": "[email protected]",
  "accessToken": "eyJhbG...",
  "refreshToken": "refresh_...",
  "expiresAt": 1711234567
}
GET /api/reports Auth Required

List bug reports for the authenticated user's account and team. Returns reports in reverse chronological order.

Query Parameters

type optional
string — Filter by type: ui, performance, crash, security, logic, data, network, accessibility, compatibility, functional, ui-ux, data-integrity, feature-request, enhancement, technical-debt, documentation, devops, ux-improvement, integration
severity optional
string — Filter by severity. Formal QA codes: s1 (Blocker), s2 (Critical), s3 (Major), s4 (Minor). Legacy values still accepted for backward compatibility: critical, high, medium, low.
status optional
string — Filter by status. Values match the dashboard's status options exactly: new, awaiting-triage, confirmed, in-progress, resolved, retesting, closed, reopened. Note the hyphens in awaiting-triage and in-progress — underscored variants will not match anything on the kanban.
resolution optional
string — Filter by resolution. Values: fixed, duplicate, works-as-designed, cannot-reproduce, will-not-fix, need-more-info.
root_cause optional
string — Exact-match filter on root cause. Common values: regression, missing-requirement, documentation, incomplete-refactor, not-a-bug, requirements-mismatch.
search optional
string — Search title + description. Also matches by exact ticket_number when the input is a short ID for the active workspace (TEST-24) or a bare positive integer (24) — see TEST-164.
limit optional
integer — Max results (default 20, max 100)
offset optional
integer — Pagination offset (default 0)

Example

cURL
curl "https://app.bugagent.com/api/reports?type=crash&limit=10" \
  -H "Authorization: Bearer ba_live_..."
POST /api/reports Auth Required

Create a new report. Supports bugs, feature requests, enhancements, technical debt, and more. If type is omitted, bugAgent auto-classifies based on title and description.

Request Body

title required
string — Bug report title (3-500 characters)
description optional
string — Detailed description
type optional
string — Report type: ui, performance, crash, security, logic, data, network, accessibility, compatibility, functional, ui-ux, data-integrity, feature-request, enhancement, technical-debt, documentation, devops, ux-improvement, integration. Auto-classified if omitted.
severity optional
string — Prefer formal QA codes: s1 (Blocker), s2 (Critical), s3 (Major, default), s4 (Minor). Legacy values still accepted: critical, high, medium, low. Auto-classifiers (heuristic + Claude) now emit s1s4.
project optional
string — Project slug or name (defaults to team's default project)
environment optional
object — { browser, os, device, url }
metadata optional
object — Arbitrary key-value metadata
format_description optional
boolean — When true, the description is reformatted into a structured bug template using AI before saving. Defaults to false.
time_spent_seconds optional
integer — Time spent in seconds. Used for tracking QA effort. The timer on the dashboard saves exact seconds. Defaults to 0.

Response

201 Created
{
  "id": "1fb72a2c-87c7-4adf-90e7-9bd81a8f34b7",
  "ticket_number": 545,
  "short_id": "WRKID-545",
  "title": "Login button unresponsive on iOS",
  "type": "ui",
  "severity": "high",
  "classification": { "type": "ui", "confidence": 0.85 },
  "quality_score": 7,
  "quality_breakdown": {
    "reproduction_steps": 0.9,
    "expected_vs_actual": 0.8,
    "environment_details": 0.7,
    "evidence": 0.6,
    "root_cause_analysis": 0.5,
    "impact_assessment": 0.8,
    "context_and_history": 0.6,
    "heuristics_and_oracles": 0.7,
    "clarity_and_structure": 0.9,
    "actionability": 0.8
  },
  "created_at": "2026-03-17T12:00:00Z",
  ...
}
POST /api/reports/format-description Auth Required

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.

Request Body

description required
string — The raw description text to format
title optional
string — Bug title for additional context when formatting

Response

200 OK
{ "formatted": "## Summary\nLogin button is unresponsive...\n\n## Steps to Reproduce\n1. Navigate to...\n\n## Expected Behavior\n...\n\n## Actual Behavior\n..." }
GET /api/reports/:id Auth Required

Retrieve a single bug report by ID. Returns 404 if the report doesn't exist or you don't have access.

ID formats: :id accepts either the UUID (e.g. 1fb72a2c-87c7-4adf-90e7-9bd81a8f34b7) or the workspace-scoped short ID (e.g. WRKID-545). Short-ID lookups are scoped to your active team — guessing another workspace's short ID returns 404.

Response Fields (includes all standard fields plus)

quality_score
integer (1–10) — Overall report quality rating calculated automatically using context-driven testing heuristics. Visual badges: red (1–3), amber (4–6), green (7–10).
quality_breakdown
object — Individual dimension scores (each 0.0–1.0): reproduction_steps, expected_vs_actual, environment_details, evidence, root_cause_analysis, impact_assessment, context_and_history, heuristics_and_oracles, clarity_and_structure, actionability.
claude_analysis, claude_pushed_at, claude_status
Final Developer Notes output. Populated by POST /claude/push, which auto-fires on bug creation. Uses the platform Anthropic key; no per-team Claude connection required. claude_status cycles analyzingdone / failed.
claude_draft_analysis, claude_challenger_critique, claude_challenger_model
First three rounds of the Developer Notes chain. claude_draft_analysis is the pre-critique Sonnet output, claude_challenger_critique is the OpenAI peer review, claude_challenger_model names the challenger. All three are null when the challenger step was skipped.
claude_rebuttal, claude_adjudicator_model
Rounds 3 & 4 of the debate path, reserved for the top-two severity buckets (s1/critical or s2/high). claude_rebuttal is Sonnet's point-by-point response to the critique; claude_adjudicator_model names the model that wrote the final claude_analysis after reading the full transcript. Both null on s3/medium or s4/low bugs (which use the cheaper three-step chain) and when the debate chain couldn't adjudicate (falls through to simple synthesis).
likely_fix_area, likely_fix_area_status, likely_fix_area_generated_at
"Likely Fix Area" output — the Developer Notes sub-block that points at specific files. Populated by POST /claude/fix-area (auto-fires on bug creation). Uses the platform Anthropic key and, when available, the team's connected GitHub repo for grounded output.
dev_notes_stale
boolean — TRUE when the description or attached media changed after the last successful Developer Notes generation. Lights up a "Regenerate?" banner in the dashboard. Cleared automatically when either POST /claude/push or POST /claude/fix-area completes successfully.
PATCH /api/reports/:id Auth Required

Update a bug report. Only fields you include in the body are updated.

ID formats: :id accepts either the UUID or the short ID (e.g. WRKID-545). Same rules apply across all /api/reports/:id verbs.

Request Body

title, description, type, severity, internal_notes
Any of these fields can be included to update. internal_notes are private and not synced to Jira.
status optional
string — Move the report on the kanban. Values match the dashboard's status options exactly: new, awaiting-triage, confirmed, in-progress, resolved, retesting, closed, reopened. The hyphens in awaiting-triage and in-progress are deliberate — underscored variants won't show up on the kanban.
resolution optional
string — The outcome of the ticket. Set whenever the row transitions out of new toward retesting / resolved / closed. Allowed values: fixed (code change shipped), duplicate (reference the canonical ticket in a comment), works-as-designed (intentional behavior), cannot-reproduce, will-not-fix (deliberately deferring), need-more-info.
root_cause optional
string — Short hyphenated tag describing the category of the underlying problem. Set alongside resolution on close. Common values today: regression, missing-requirement, documentation, incomplete-refactor, not-a-bug, requirements-mismatch. Open-ended — extend the taxonomy when a new pattern shows up in 2+ tickets. Used by analytics and the agent-loop training corpus.
assigned_to optional
UUID — User ID to assign this bug to. Must be an active team member. Set to null to unassign. When the assignee changes, a database trigger fires the in-app bell notification AND emails the new assignee — same path is used by the MCP update_bug_report tool and any raw SQL UPDATE, so notifications are consistent regardless of entry point. Email respects the per-user opt-out at notification_preferences.email_bug_report_assignment (default on). When status=retesting, the email uses retest-handoff wording.
time_spent_seconds optional
integer — Time spent in seconds. Used for tracking QA effort. The timer on the dashboard saves exact seconds.
POST /api/reports/assign Auth Required

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.

Request Body

report_id required
UUID or short ID (e.g. WRKID-545) — The bug report to assign.
assigned_to required
UUID — The user ID to assign to. Must be an active member of the same workspace.

Response

Returns success: true and notified: true if the bell-icon notification was inserted. The email send is fire-and-forget — the response returns before it completes. The assignee can mute the email channel at /dashboard/settings#notifications (in-app bell is always shown).

POST /api/reports/upload Auth Required

Upload file attachments (screenshots, screen recordings, audio memos, documents) to a bug report. Uses multipart/form-data.

Form Fields

reportId required
string — ID of the report to attach files to
files required
File[] — Up to 10 files per request, 400 MB per file, 400 MB total. Accepted MIME types: any image/* (png, jpeg, gif, webp, heic, avif, svg), any video/* (mp4, webm, quicktime, mpeg), any audio/* (mp3, wav, m4a, ogg, webm), application/pdf, text/plain, text/csv, text/markdown, application/json.
POST /api/reports/flush Auth Required

Bulk delete old bug reports. Requires owner or admin team role.

Request Body

months required
integer — Delete reports older than N months (1, 2, or 3)
project_id optional
string — Scope to a specific project

Response

200 OK
{ "deleted": 42 }
POST /api/reports/comments Auth Required

Add a comment to a bug report, or toggle an emoji reaction on a comment.

Create Comment

report_id required
string — Report to comment on
content required
string — Comment text (max 10,000 characters)

Toggle Reaction

action required
"react"
comment_id required
string — Comment to react to
emoji required
string — Emoji character
PATCH /api/reports/comments Auth Required

Edit a comment. Only the author can edit their own comments.

comment_id required
string
content required
string — Updated comment text
DELETE /api/reports/comments Auth Required

Delete a comment. Only the author can delete their own comments.

comment_id required
string
GET /api/projects Auth Required

List all projects for your team.

POST /api/projects Auth Required

Create a new project. The first project is automatically set as default. Subject to plan limits.

Request Body

name required
string — Project name (auto-generates slug)
description optional
string
is_default optional
boolean — Set as default project for new reports
PATCH /api/projects Auth Required

Update a project's name, description, or default status.

id required
string — Project ID
name, description, is_default
Fields to update
DELETE /api/projects Owner / Manager

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.

Request Body (JSON)

id required
string — Project ID to delete

What Gets Deleted

⚠️
This action is irreversible. The following data is permanently removed:
  • All bug reports and their media attachments (storage freed)
  • Web automations, runs, and schedules
  • Mobile apps, automations, runs, and schedules (binaries purged from storage)
  • Test cases, test suites, test runs, and results
  • Geo-Snap screenshots
  • Notes and time tracking entries

Important Behavior

Team members
All team members remain in the workspace and retain access to other projects.
Default project
If the deleted project was the default, the oldest remaining project is automatically promoted.
Storage quota
Team storage usage is decremented by the total size of purged files (attachments + mobile app binaries).

Errors

403
Only owners and managers can delete projects.
400
Cannot delete your last project. You must always have at least one.
404
Project not found or does not belong to this team.
GET /api/keys Auth Required

List your active (non-revoked) API keys. Only the key prefix is returned — full keys are shown once at creation.

POST /api/keys Auth Required

Generate a new API key. The full key (starting with ba_live_) is returned only once — store it securely.

Request Body

name required
string — A label for this key (e.g., "CI Pipeline")
scopes optional
string[] — Defaults to ["reports:read", "reports:write"]

Response

201 Created
{
  "key": "ba_live_abc123...",
  "id": "uuid",
  "name": "CI Pipeline",
  "key_prefix": "ba_live_abc123",
  "scopes": ["reports:read", "reports:write"],
  "created_at": "2026-03-17T12:00:00Z"
}
DELETE /api/keys/:id Auth Required

Revoke an API key. The key is soft-deleted and can no longer be used for authentication.

POST /api/keys/:id/regenerate Auth Required

Revoke the current key and generate a new one with the same name and scopes. Returns the new full key.

GET /api/profile Auth Required

Get the authenticated user's profile.

Response

200 OK
{
  "id": "uuid",
  "email": "[email protected]",
  "full_name": "Jane Smith",
  "avatar_url": null,
  "plan": "pro",
  "is_agent": false,
  "created_at": "2026-03-10T08:00:00Z"
}
PATCH /api/profile Auth Required

Update your profile. Only full_name can be changed via the API (security: no email, role, or plan changes).

full_name
string — Display name
POST /api/profile/password Auth Required

Change your password.

password required
string — New password (min 8 characters)
GET /api/settings Auth Required

Get your profile info and notification preferences.

Response

200 OK
{
  "fullName": "Jane Smith",
  "email": "[email protected]",
  "isAgent": false,
  "plan": "pro",
  "notifications": {
    "emailUsageWarning": true,
    "emailBugReportAssignment": true,
    "emailTestCaseAssignment": true
  }
}
PATCH /api/settings Auth Required

Update notification preferences.

email_usage_warning optional
boolean
GET /api/usage Auth Required

Get your current plan usage (reports used, limit, remaining, reset date).

Response

200 OK
{
  "used": 47,
  "limit": 5000,
  "remaining": 4953,
  "plan": "pro",
  "period": "monthly",
  "resetsAt": "2026-04-01T00:00:00Z"
}
GET /api/stats Auth Required

Get report statistics with daily counts, breakdowns by type, severity, and status.

Query Parameters

days optional
integer — Number of days to look back (default 30, max 365)

Response

200 OK
{
  "period": "30 days",
  "total": 127,
  "daily": [
    { "date": "2026-02-16", "count": 3 },
    { "date": "2026-02-17", "count": 5 },
    ...
  ],
  "byType": { "ui": 42, "crash": 18, "logic": 67, "feature-request": 12, "enhancement": 8 },
  "bySeverity": { "high": 23, "medium": 89, "low": 15 },
  "byStatus": { "open": 95, "resolved": 32 }
}
POST /api/checkout Auth Required

Create a Stripe checkout session to upgrade your plan. Returns a URL to redirect the user to.

Request Body

plan required
string — "pro" or "team"
billing optional
string — "monthly" (default) or "annual"

Response

200 OK
{ "url": "https://checkout.stripe.com/..." }
POST /api/billing-portal Auth Required

Get a URL to the Stripe billing portal where users can manage subscriptions, payment methods, and invoices.

Response

200 OK
{ "url": "https://billing.stripe.com/..." }
POST /dashboard/settings/team Manager+

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.

Form Data

action required
string — "invite"
email required
string — Email address to invite
role optional
string — "contributor" (default), "manager", or "owner". Managers can only invite contributors and managers.
👥
Users already in your workspace cannot be invited again. Users in different workspaces can be invited — they will belong to multiple workspaces. Pending invitations appear inline in the members list with a pending status badge. Managers can resend (resets the 5-day expiry) or cancel invitations. Cancelled invitations become invalid immediately.
POST /dashboard/settings/team Manager+

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.

Form Data

action required
string — "edit_name"
member_id required
string — Team member record ID
new_name required
string — New display name (1-200 characters)
POST /dashboard/settings/team Manager+

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.

Form Data

action required
string — "change_role"
member_id required
string — Team member record ID
new_role required
string — "contributor", "manager", or "admin"
POST /dashboard/settings/team Owner / Manager

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.

Form Data

action required
string — "remove_member"
member_id required
string — Team member record ID
👥
Removed users who are later re-invited will rejoin the workspace with a fresh membership. Their ghost profile ensures historical records (e.g. “created by”) remain intact.
POST /api/switch-team Auth Required

Switch the active workspace context. Users who belong to multiple workspaces use this to change which workspace's data they see.

Request Body (JSON)

teamId required
string — Team ID to switch to (must be a team the user belongs to)
POST /api/create-team Auth Required

Create a new workspace. Available to all plan tiers (Free, Pro, Team, Enterprise) with a 100-workspace cap per user. New workspaces start on the free plan and can be upgraded via billing. If no name is provided, a unique uplifting name is auto-generated (e.g. "Radiant Foxes"). A Default Project is automatically created so the workspace always has at least one project.

Request Body (JSON)

name optional
string — Custom workspace name. Must be 2–50 characters. Auto-generated if omitted.

Response

{
  "success": true,
  "team": {
    "id": "uuid",
    "name": "Radiant Foxes",
    "plan": "pro"
  }
}

Errors

403
Workspace limit reached (100 max per user). Delete an existing workspace or contact support.
409
A workspace with that name already exists.
400
Name must be between 2 and 50 characters.
Skills & Integrations

Skills Overview

bugAgent Skills connect external tools into your QA workflow via OAuth or API keys. Each Skill enriches the context engine with data from services your team already uses — syncing code, pulling analysis, and bridging issue trackers.

Available Skills

SkillConnectionDescription
GitHubOAuthSync repositories for Playwright automation scripts. See GitHub endpoints.
JiraOAuth 2.0Bi-directional bug sync with comments, attachments, and severity. See Jira endpoints.
ClaudeAPI KeyRoot cause analysis for bug reports. See Claude endpoints.
SlackWebhookNotifications for new reports, automation results, and team activity.

Enabling Skills

Navigate to Settings → Integrations in the dashboard. Each Skill has its own connection flow. Once connected, configure per-project settings such as auto-push for Claude, repository mapping for GitHub, or default project for Jira.

Building Custom Skills

Partners can build custom Skills that interact with bugAgent programmatically. Use the REST API endpoints documented below — reports, automation, projects, and team management — to create integrations that feed data into or out of bugAgent. To propose a new Skill, contact [email protected].

👥
Jira connections are team-scoped. One connection is shared by all team members. Any member can sync reports; only managers and above can connect, configure, or disconnect. If the connecting user leaves, the integration persists. Auto-sync polls Jira periodically for changes and syncs comments, attachments, and severity (last-updated-wins) automatically. Use the force sync button on any report for an immediate full sync.
GET /api/jira/connect Manager+

Start the Jira OAuth 2.0 flow. Redirects to Atlassian's authorization page. The resulting connection is stored against the user's active team, not the individual user.

🔗
This is a browser redirect endpoint, not a JSON API. Navigate to it directly from a browser.
GET /api/jira/projects Auth Required

Fetch all Jira projects accessible by the team's shared connection. Used to populate the project dropdown when pushing a report to Jira for the first time. Returns the default project key if one is saved.

Response

{
  "projects": [
    { "id": "10001", "key": "BUG", "name": "Bug Tracker", "avatar": "https://..." },
    { "id": "10002", "key": "PROJ", "name": "Main Project", "avatar": "https://..." }
  ],
  "defaultProjectKey": "BUG"
}
POST /api/jira/sync Auth Required

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.

Request Body

reportId required
string — Bug report ID
projectKey optional
string — Jira project key (uses default if omitted)

Response

200 OK
{
  "success": true,
  "jiraKey": "PROJ-1234",
  "jiraUrl": "https://your-site.atlassian.net/browse/PROJ-1234"
}
POST /api/jira/force-sync Auth Required

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.

Request Body

reportId required
string — Bug report ID (must already be linked to a Jira issue)

Sync Behavior

Severity / Priority
Last updated wins. Compares report.updated_at vs Jira fields.updated — whichever platform was modified more recently determines the value. The other side is updated automatically.
Description & Title
Pushed from bugAgent to Jira. Environment metadata is appended if not already structured.
Comments
Bi-directional. Local comments without a jira_comment_id are pushed to Jira. Jira comments not in bugAgent are pulled in. Comments originating from bugAgent (prefixed [bugAgent]) are not re-imported.
Media / Attachments
Bi-directional. Local media is uploaded to Jira; Jira attachments are downloaded to Supabase storage. Deduplicated by filename (case-insensitive) and jira_attachment_id to prevent duplicates across sync cycles.

Response

200 OK
{
  "success": true,
  "results": [
    "Description synced",
    "Priority pushed to Jira (high — last updated)",
    "2 comment(s) pushed to Jira",
    "1 attachment(s) pulled from Jira"
  ]
}
GET /api/jira/check Auth Required

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.

Query Parameters

reportId required
string — Bug report ID (must have a jira_key)

Response

200 OK
{
  "hasChanges": true,
  "jiraUpdatedAt": "2026-03-18T15:30:00.000Z",
  "changes": {
    "title": { "jira": "Updated title in Jira", "bugagent": "Original title" },
    "severity": { "jira": "high", "bugagent": "medium" }
  }
}
POST /api/jira/merge Auth Required

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.

Request Body

reportId required
string — Bug report ID
merged required
object — Fields to sync: title, severity, status, type. Each value is the chosen winner (from bugAgent or Jira).
POST /api/jira/settings Manager+

Update the team's Jira integration settings or disconnect. Only managers and above can modify these settings.

Request Body

action required
string — "update_settings" or "disconnect"
auto_push optional
boolean — Auto-sync new reports to Jira
default_project_key optional
string — Default Jira project key
POST /api/jira/batch-sync-status Auth Required

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.

Request Body

report_ids required
array of UUIDs — The bug report IDs to sync with Jira

Response

200 OK
{
  "updates": [
    {
      "id": "uuid",
      "old_status": "new",
      "new_status": "in-progress",
      "jira_status": "In Progress"
    }
  ],
  "synced": 1
}
Automation
🤖
Automation requires a Pro or Team plan. Record browser actions via the FAB SDK, generate Playwright scripts with AI, or create automations directly with a custom Playwright script. Run them on demand, scheduled, or in CI/CD pipelines. Pro and Team plans also include an automation coverage mind map on the dashboard.
POST /api/automations Auth Required

Create a new automation. Stores the recorded steps, generated Playwright script, and optional schedule. The automation is scoped to the user's active team.

Request Body

name required
string — Human-readable name for the automation
steps required
array — Recorded browser action steps from the FAB SDK
script optional
string — Generated Playwright script (can also be generated later via /generate-script)
schedule optional
string — Cron expression for scheduled runs (e.g. "0 9 * * 1-5")
start_url optional
string — URL where the automation begins

Response

201 Created
{
  "id": "uuid",
  "name": "Login flow smoke test",
  "steps": [ ... ],
  "script": "import { test } from '@playwright/test'; ...",
  "schedule": "0 9 * * 1-5",
  "start_url": "https://app.example.com/login",
  "status": "active",
  "team_id": "uuid",
  "created_by": "uuid",
  "created_at": "2026-03-21T12:00:00Z",
  "updated_at": "2026-03-21T12:00:00Z"
}
POST /api/automations/create Auth Required

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.

Supported languages: Node.js/JavaScript/TypeScript and Python. The runner auto-detects the language from the script's imports (from playwright/import playwright → Python; otherwise Node). Python scripts run under pytest if they define a def test_* function, otherwise under python3. Other languages (C#, Java, etc.) aren't supported yet.

Duplicating an Automation

To duplicate an existing automation, fetch its details via GET /api/automations/:id, then call this endpoint with the original's script, target_url, and project_id. Set name to "[Copy] Original Name". The duplicate starts in "draft" status and does not inherit version history or run history from the original.

Request Body

name required
string — Name of the automation
target_url optional
string — Target URL the script tests. If omitted, automatically derived from the first absolute URL in a page.goto(...) call in the script.
script optional
string — Playwright test script content. Node.js/JavaScript/TypeScript or Python — language is auto-detected. Defaults to a placeholder template if not provided.
status optional
string — "active" or "draft". Default: "draft"
project_id optional
string — Project to assign the automation to. Uses the default project if omitted.

Authentication

Requires session authentication (dashboard) or an API key with automations:write scope.

Plan

Pro or Team plan required.

Response

201 Created
{
  "id": "uuid"
}
GET /api/automations Auth Required

List all automations for the user's active team. Supports pagination and filtering by status.

Query Parameters

status optional
string — Filter by status: active, paused, archived
project_id optional
string — Filter automations by project ID
created_by optional
string — Filter automations by creator's user ID
page optional
number — Page number (default: 1)
limit optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "automations": [
    {
      "id": "uuid",
      "name": "Login flow smoke test",
      "status": "active",
      "project_id": "proj_uuid",
      "created_by": "user_uuid",
      "schedule": "0 9 * * 1-5",
      "last_run_at": "2026-03-21T09:00:00Z",
      "last_run_status": "passed",
      "run_count": 42,
      "created_at": "2026-03-20T12:00:00Z"
    }
  ],
  "total": 5,
  "page": 1,
  "limit": 20
}
GET /api/automations/:id Auth Required

Get full details of a single automation, including recorded steps, generated Playwright script, schedule, recent run history, and saved script version history (the stack that version_index on the run endpoints indexes into).

Response

200 OK
{
  "id": "uuid",
  "name": "Login flow smoke test",
  "steps": [
    { "action": "navigate", "url": "https://app.example.com/login" },
    { "action": "fill", "selector": "#email", "value": "[email protected]" },
    { "action": "fill", "selector": "#password", "value": "********" },
    { "action": "click", "selector": "button[type=submit]" },
    { "action": "assert", "selector": ".dashboard-title", "text": "Welcome" }
  ],
  "script": "import { test, expect } from '@playwright/test'; ...",
  "script_versions": [
    { "script": "// older text of the script", "source": "manual_edit", "timestamp": "2026-03-20T13:42:00Z" },
    { "script": "// the state before an AI-optimize ran", "source": "before_optimize", "timestamp": "2026-03-21T07:50:00Z" }
  ],
  "schedule": "0 9 * * 1-5",
  "start_url": "https://app.example.com/login",
  "status": "active",
  "team_id": "uuid",
  "created_by": "uuid",
  "created_at": "2026-03-20T12:00:00Z",
  "updated_at": "2026-03-21T08:00:00Z",
  "recent_runs": [
    { "id": "uuid", "status": "passed", "duration_ms": 4200, "started_at": "2026-03-21T09:00:00Z", "script_version_label": "current", "script_version_source": "current" }
  ]
}

Notes

  • script is always the current live version — the one Run Now executes when version_index is omitted.
  • script_versions is a chronological stack (oldest first) of up to 100 prior states of script. An entry is pushed whenever script changes via PATCH, optimize, or the BrowserStack rewrite. Each entry records { script, source, timestamp }; valid source values are manual_edit, before_optimize, bs_compat_rewrite. Callers index into this array with version_index on POST /automations/runs or POST /v1/automations/run to replay a historical version. Omit version_index to run script (current).
  • Each entry in recent_runs carries the version it executed as script_version_label ("current" or "vN") and script_version_source. The per-run script text itself is on the run detail (script_snapshot) — omitted from list responses to keep them small.
PATCH /api/automations/:id Auth Required

Update an existing automation. Only provided fields are changed. Use this to rename, update the script, change the schedule, or pause/resume.

Request Body

name optional
string — New name
steps optional
array — Updated recorded steps
script optional
string — Updated Playwright script. If the value differs from the stored script, the previous text is pushed onto script_versions (capped at 100 entries, oldest-evicted) so callers can replay or undo it.
version_source optional
string — Label stored with the entry that gets pushed onto script_versions when script changes. Defaults to "manual_edit". The dashboard uses "bs_compat_rewrite" when accepting the BrowserStack rewrite; the optimize endpoint internally uses "before_optimize". Ignored when script is unchanged.
schedule optional
string | null — Cron expression or null to remove schedule
status optional
string — active, paused, or archived
auth_enabled optional
boolean — When true, the runner logs in before executing the script. Requires a stored password; auth_username is optional (leave blank for password-only gates).
auth_signin_url optional
string | null — URL of the signin page. If null, the runner uses the script's first page.goto().
auth_username optional
string | null — Username or email for the pre-auth step. Leave blank for password-only gates (single front-door password with no username field).
auth_password optional
string — New password. Encrypted at rest with AES-256-GCM using AUTOMATIONS_AUTH_KEY. Pass an empty string to clear the stored password; omit to leave it unchanged. Never returned in responses.

Response

200 OK
{
  "id": "uuid",
  "name": "Login flow smoke test (updated)",
  "status": "paused",
  "schedule": null,
  "updated_at": "2026-03-21T14:00:00Z"
}

Notes

  • GET responses include auth_has_password: true|false so clients can render a "password set" indicator without ever seeing the ciphertext.
  • Pre-auth runs a heuristic login: navigate to auth_signin_url, fill the password field (and username/email if provided) via role/type selectors, submit, wait for networkidle. Supports username+password flows (including two-step email → Next → password) and password-only front-door gates.
  • When the heuristic selectors don't match the signin page (e.g. non-standard input names, custom-element wrappers), the runner falls back to a Claude-assisted locator plan. The runner captures the signin form HTML, ships it to Claude (with "USERNAME" / "PASSWORD" placeholders so credentials never hit the model), receives a structured step list (fill / click / wait), and executes it. No additional user configuration required — this happens automatically on every pre-auth miss.
  • On BrowserStack (including iOS Playwright), pre-auth runs as a test.beforeEach hook injected into the remote browser's test — a storage-state handoff from a local Chromium would fail because of IP pinning and engine mismatch. The injected prologue uses either the heuristic or the Claude-built plan, whichever the runner's local probe determined would work on this particular signin page.
DELETE /api/automations/:id Auth Required

Permanently delete an automation and all its associated run history. This action cannot be undone.

Response

200 OK
{ "deleted": true }
POST /api/automations/generate-script Auth Required

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.

Request Body

steps required
array — Recorded browser action steps from the FAB SDK
automation_id optional
string — If provided, saves the generated script to this automation
start_url optional
string — Starting URL for the test

Response

200 OK
{
  "script": "import { test, expect } from '@playwright/test';\n\ntest('Login flow smoke test', async ({ page }) => {\n  await page.goto('https://app.example.com/login');\n  await page.fill('#email', '[email protected]');\n  await page.fill('#password', '********');\n  await page.click('button[type=submit]');\n  await expect(page.locator('.dashboard-title')).toContainText('Welcome');\n});",
  "automation_id": "uuid"
}
POST /api/automations/:id/optimize Auth Required

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.

URL Parameters

id required
string — The automation ID to optimize

Response

200 OK
{
  "script": "// optimized Playwright script...",
  "version": 3,
  "changes_summary": "Fixed 4 issues: replaced fragile text selectors with data-testid, added explicit waitForLoadState, improved assertions with toBeVisible, added error recovery for auth flow."
}
POST /api/automations/:id/undo Auth Required

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 version_index to POST /automations/runs instead.

URL Parameters

id required
string — The automation ID to undo

Response

200 OK
{
  "script": "// previous version of the script...",
  "version": 2,
  "versions_remaining": 1
}
400 Bad Request
{ "error": "No previous versions available to undo" }
POST /api/automations/:id/rewrite-for-bs Auth Required

Rewrite a script-style Python Playwright script (sync_playwright() + browser.new_page() + if __name__ == "__main__":) into pytest-style (def test_<name>(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).

Request Body

script required
string — The current script contents (up to 20,000 chars). Pass whatever's in the editor, not what's stored, so unsaved edits get converted too.

Response

200 OK
{
  "rewritten": "from playwright.sync_api import Page, expect\n\ndef test_navigate_and_wait(page: Page):\n    page.goto(\"https://example.com\")\n    ..."
}

Notes

  • The dashboard shows an amber "Not compatible with bugAgent Live" banner on the automation detail page when a Python script lacks a def test_*(page): function, with a one-click rewrite button that calls this endpoint.
  • Requires ANTHROPIC_API_KEY on the Dashboard service. Returns 500 with a clear error if not configured.
  • Markdown code fences in Claude's output are stripped defensively before returning — the response is always raw Python source, never wrapped in ```python … ```.
  • Does not modify stored data. To persist the rewrite, follow up with PATCH /api/automations/:id including "script": <rewritten> and (optionally) "version_source": "bs_compat_rewrite" so the entry shows up in version history with that tag.
POST /api/automations/runs Auth Required

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.

Request Body

automation_id required
string — Automation to run
version_index optional
integer — Replay a prior entry from the automation's script_versions stack instead of the live script. Default: the current live script runs whenever this field is omitted or null. The history holds up to 100 entries, oldest first — pass 0 for the oldest, script_versions.length - 1 for the newest prior entry. Non-integer, negative, or out-of-range values return 400; we never silently fall back to current so a stale client can't ship the wrong version by accident. The resolved script is snapshotted onto the run record so it stays recoverable after further edits or stack eviction. Call GET /automations/:id first if you need to inspect the available versions.
environment optional
object — Environment variables passed to the Playwright script
device optional
string — Device to emulate during the test run (Virtual mode). Controls viewport size, user agent, and mobile mode. Default: "desktop"
browserstack optional
boolean — Set to true to run on a real BrowserStack browser instead of the local runner. Requires bs_browser, bs_os, bs_os_version. Node.js scripts support desktop, real Android, and real iPhone; Python scripts support desktop only (real mobile on Python isn't supported by BrowserStack's Playwright product yet). After the run completes the runner fetches BrowserStack's session video and re-hosts it on our own storagevideo_url on the resulting run record points at the re-hosted URL so end users can watch the video in the dashboard without needing a BrowserStack login of their own. A Playwright trace .zip is also captured and published at results.trace_url (open in https://trace.playwright.dev/?trace=<url>).
bs_browser optional
string — BrowserStack browser: "chrome", "firefox", "safari", "edge"
bs_os optional
string — BrowserStack OS: "Windows", "OS X", "android" for a real Android device, or "ios" for a real iPhone.
bs_os_version optional
string — For desktop: OS version ("11" on Windows, "Sonoma" on macOS). For bs_os: "android": the real Android device name ("Samsung Galaxy S25 Ultra", "Google Pixel 10", "OnePlus 13R"). For bs_os: "ios": the real iPhone model ("iPhone 17 Pro Max", "iPhone 16 Pro Max", "iPhone 15 Pro Max"). The runner pairs each device with the correct BrowserStack osVersion automatically. Node.js/TypeScript scripts run via the BrowserStack Node SDK (browserstack-node-sdk) — covers desktop, real Android, and real iPhone. Python scripts run via the BrowserStack Python SDK (browserstack-sdk pip, pytest-playwright) — desktop only. Real mobile on Python isn't supported yet: browser_type.connect() can't drive BrowserStack's real-mobile endpoints the way the Node SDK's _android.connect() / webkit.connect() do.
Available device values
CategoryValueViewport
Laptops & Desktopsdesktop1280×800
macbook-air1440×900
macbook-pro-141512×982
macbook-pro-161728×1117
dell-xps-131280×800
chromebook1366×768
1920x10801920×1080
2560x14402560×1440
3840x21603840×2160
iPhonesiphone-15-pro-max430×932
iphone-15393×852
iphone-14-pro-max430×932
iphone-14390×844
iphone-13390×844
iphone-12390×844
iphone-se375×667
Androidpixel-7412×915
pixel-6412×915
galaxy-s23360×780
galaxy-s22360×780
galaxy-s21360×800
galaxy-a54360×800
oneplus-11412×915
Tabletsipad768×1024
ipad-air820×1180
ipad-mini768×1024
ipad-pro1024×1366
galaxy-tab-s8800×1280
surface-pro912×1368

Response

202 Accepted
{
  "run_id": "uuid",
  "automation_id": "uuid",
  "status": "queued",
  "device": "desktop",
  "queued_at": "2026-03-21T14:30:00Z"
}

Notes

  • Self-healing locators (automatic). Every Node Playwright run is wrapped so that when a locator action (click, fill, press, hover, etc.) times out, the runner captures the page's interactive DOM, asks Claude for a better CSS selector, and retries the same action once with the replacement. The healing happens in the Node test worker on our runner — no secrets leak to the browser. Applies to both local and BrowserStack runs. Healing events are surfaced in the run's stdout as [bugAgent] self-healed locator: "<original>" → "<replacement>". Assertions (expect().toBeVisible() etc.) are not healed — a failed assertion still fails the test.
  • Pre-auth (if configured on the automation) runs before the test. See PATCH /automations/:id for details.
GET /api/automations/runs Auth Required

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=<url> for a full DOM-timeline replay.

Query Parameters

automation_id optional
string — Filter runs for a specific automation
status optional
string — Filter by status: queued, running, passed, failed, cancelled
page optional
number — Page number (default: 1)
limit optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "runs": [
    {
      "id": "uuid",
      "automation_id": "uuid",
      "automation_name": "Login flow smoke test",
      "status": "passed",
      "duration_ms": 4200,
      "started_at": "2026-03-21T09:00:00Z",
      "finished_at": "2026-03-21T09:00:04Z",
      "error": null,
      "video_url": "https://storage.bugagent.com/runs/uuid/bs-session.mp4",
      "script_version_label": "current",
      "script_version_source": "current",
      "script_snapshot": "// full Playwright source the runner executed",
      "results": {
        "trace_url": "https://storage.bugagent.com/runs/uuid/trace.zip"
      }
    },
    {
      "id": "uuid",
      "automation_id": "uuid",
      "automation_name": "Checkout flow",
      "status": "failed",
      "duration_ms": 8500,
      "started_at": "2026-03-21T09:00:00Z",
      "finished_at": "2026-03-21T09:00:08Z",
      "error": "Timeout waiting for selector '#confirm-btn'",
      "video_url": "https://storage.bugagent.com/runs/uuid/video.webm",
      "results": {
        "trace_url": "https://storage.bugagent.com/runs/uuid/trace.zip",
        "stdout": "...",
        "stderr": "..."
      }
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20
}
POST /api/v1/automations/run API Key

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.

Request Body

automation_id required
string — Automation to run
version_index optional
integer — Replay a historical entry from the automation's script_versions stack (0 = oldest). Default: the current live script runs whenever this field is omitted or null. Non-integer, negative, or out-of-range values return 400. The run record captures the resolved script snapshot plus script_version_label and script_version_source, and any bug report auto-created from a failed run deep-links back to this exact version in the editor via ?version_index=N.
environment optional
object — Environment variables passed to the Playwright script
callback_url optional
string — Webhook URL to POST results to when the run completes
device optional
string — Device to emulate during the test run. Controls viewport size, user agent, and mobile mode. Default: "desktop". See POST /automations/runs for the full list of supported device values.

Headers

Authorization required
Bearer ba_live_... — API key with automations:run scope

Response

202 Accepted
{
  "run_id": "uuid",
  "automation_id": "uuid",
  "status": "queued",
  "device": "desktop",
  "poll_url": "/api/v1/automations/runs/uuid",
  "queued_at": "2026-03-21T14:30:00Z"
}
GET /api/v1/automations/runs/:id API Key

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.

Headers

Authorization required
Bearer ba_live_... — API key with automations:run scope

Response

200 OK
{
  "id": "uuid",
  "automation_id": "uuid",
  "automation_name": "Login flow smoke test",
  "status": "passed",
  "duration_ms": 4200,
  "started_at": "2026-03-21T09:00:00Z",
  "finished_at": "2026-03-21T09:00:04Z",
  "error": null,
  "trace_url": "https://bugagent.com/traces/uuid",
  "screenshots": [
    { "step": "login-success", "url": "https://storage.bugagent.com/screenshots/uuid.png" }
  ]
}
GitHub Integration
GitHub integration is available on Pro and Team plans. Connect your GitHub account to automatically push Playwright automation scripts to your repositories. Scripts are saved to tests/bugagent/{name}.spec.ts and stay in sync on every edit.
POST /api/github/connect Auth Required

Initiate the GitHub OAuth flow. Returns a redirect URL that opens the GitHub authorization page. Once the user authorizes, GitHub redirects back to bugAgent with an installation token. Connect from Settings → Integrations in the dashboard.

Request Body

redirect_uri optional
string — Custom redirect URI after OAuth completes. Defaults to the dashboard integrations page.

Response

200 OK
{
  "authorization_url": "https://github.com/login/oauth/authorize?client_id=...",
  "state": "random-state-token"
}
GET /api/github/repos Auth Required

List all GitHub repositories accessible via the connected GitHub account. Use this to select which repo to map to a bugAgent project.

Response

200 OK
{
  "repos": [
    {
      "id": 123456,
      "full_name": "acme/web-app",
      "default_branch": "main",
      "private": true
    }
  ]
}
POST /api/github/mapping Auth Required

Map a bugAgent project to a GitHub repository. Once mapped, Playwright automation scripts created or updated in that project are automatically pushed to tests/bugagent/{name}.spec.ts in the target repo. Deleting or archiving an automation removes the file from the repo.

Request Body

project_id required
string — The bugAgent project ID to map
repo_full_name required
string — GitHub repo in owner/repo format (e.g. acme/web-app)
branch optional
string — Target branch. Defaults to the repo's default branch.
path_prefix optional
string — Directory path for scripts. Defaults to tests/bugagent.

Response

200 OK
{
  "mapping_id": "uuid",
  "project_id": "uuid",
  "repo_full_name": "acme/web-app",
  "branch": "main",
  "path_prefix": "tests/bugagent",
  "created_at": "2026-03-22T10:00:00Z"
}
GET /api/github/status Auth Required

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.

Response

200 OK
{
  "connected": true,
  "github_username": "acme-dev",
  "mappings": [
    {
      "project_id": "uuid",
      "project_name": "Web App",
      "repo_full_name": "acme/web-app",
      "branch": "main",
      "path_prefix": "tests/bugagent",
      "last_sync_at": "2026-03-22T09:45:00Z",
      "sync_status": "ok"
    }
  ]
}

Notes

  • If sync_status is sha_conflict, the remote file was modified outside bugAgent. Use POST /api/github/mapping to re-map and force a fresh push, or resolve the conflict in GitHub first.
  • Scripts are pushed to {path_prefix}/{automation-name}.spec.ts using the GitHub Contents API.
DELETE /api/github/disconnect Auth Required

Disconnect the GitHub integration. Removes the OAuth token and all project-to-repo mappings. Scripts already pushed to GitHub are not deleted.

Response

200 OK
{
  "disconnected": true
}
Notes
📝
Notes are available on all plans (Free, Pro, Team). Capture testing observations, meeting notes, and session logs in 5 formats: Markdown, Plain Text, Rich Text, Checklist, and Outline. Includes voice-to-text dictation, time tracking, file attachments, and private or shared visibility.
GET /api/notes Auth Required

List notes for the authenticated user's team. Returns notes the user owns or notes with shared visibility. Supports keyword search, project filtering, author filtering, and date range filtering.

Query Parameters

q optional
string — Keyword search across note titles and content
project_id optional
string — Filter by project UUID
author_id optional
string — Filter by author's user UUID
from optional
string — ISO 8601 date. Only return notes created on or after this date
to optional
string — ISO 8601 date. Only return notes created on or before this date
page optional
number — Page number (default: 1)
per_page optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "notes": [
    {
      "id": "uuid",
      "title": "Checkout flow observations",
      "format": "markdown",
      "visibility": "shared",
      "project_id": "uuid",
      "author_id": "uuid",
      "author_name": "Jane Smith",
      "time_spent_seconds": 1820,
      "attachments_count": 2,
      "created_at": "2026-03-22T10:00:00Z",
      "updated_at": "2026-03-22T10:30:00Z"
    }
  ],
  "total": 42,
  "page": 1,
  "per_page": 20
}
POST /api/notes Auth Required

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.

Request Body

title optional
string — Note title (max 200 chars). Auto-generated from first 30 characters of content if omitted
content optional
string — Note content (max 100,000 chars)
format optional
string — markdown (default), plain_text, rich_text, checklist, outline
visibility optional
string — private (default) or shared. Private notes are only visible to the author. Shared notes are visible to all team members
project_id optional
string — Associate the note with a project UUID
time_spent_seconds optional
integer — Time spent in seconds. Used for tracking QA effort. The timer on the dashboard saves exact seconds. Defaults to 0

Response

201 Created
{
  "id": "uuid",
  "title": "Checkout flow observations",
  "content": "## Session 1\n- Payment form loads slowly...",
  "format": "markdown",
  "visibility": "private",
  "project_id": "uuid",
  "author_id": "uuid",
  "time_spent_seconds": 0,
  "attachments": [],
  "created_at": "2026-03-22T10:00:00Z",
  "updated_at": "2026-03-22T10:00:00Z"
}
GET /api/notes/:id Auth Required

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.

Response

200 OK
{
  "id": "uuid",
  "title": "Checkout flow observations",
  "content": "## Session 1\n- Payment form loads slowly on 3G...",
  "format": "markdown",
  "visibility": "shared",
  "project_id": "uuid",
  "author_id": "uuid",
  "author_name": "Jane Smith",
  "time_spent_seconds": 1820,
  "attachments": [
    {
      "id": "uuid",
      "filename": "screenshot.png",
      "size_bytes": 245000,
      "mime_type": "image/png",
      "url": "https://storage.bugagent.com/notes/uuid/screenshot.png"
    }
  ],
  "created_at": "2026-03-22T10:00:00Z",
  "updated_at": "2026-03-22T10:30:00Z"
}
PATCH /api/notes/:id Auth Required

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).

Request Body

title optional
string — Updated title
content optional
string — Updated content
format optional
string — Change format: markdown, plain_text, rich_text, checklist, outline
visibility optional
string — private or shared
project_id optional
string — Re-associate to a different project
time_spent_seconds optional
integer — Time spent in seconds. Used for tracking QA effort. The timer on the dashboard saves exact seconds.

Response

200 OK
{
  "id": "uuid",
  "title": "Checkout flow observations (updated)",
  "content": "...",
  "format": "markdown",
  "visibility": "shared",
  "time_spent_seconds": 2400,
  "updated_at": "2026-03-22T11:00:00Z"
}
DELETE /api/notes/:id Auth Required

Permanently delete a note and all its attachments. Only the note author can delete a note. This action cannot be undone.

Response

200 OK
{ "deleted": true }
POST /api/notes/upload Auth Required

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.

Request Body (multipart/form-data)

note_id required
string — The note UUID to attach the file to
file required
file — The file to upload (max 400 MB)

Response

200 OK
{
  "id": "uuid",
  "note_id": "uuid",
  "filename": "walkthrough.mp4",
  "size_bytes": 52428800,
  "mime_type": "video/mp4",
  "url": "https://storage.bugagent.com/notes/uuid/walkthrough.mp4"
}

Notes

  • Accepted MIME types: any image/* (png, jpeg, gif, webp, heic, avif, svg), any video/* (mp4, webm, quicktime, mpeg), any audio/* (mp3, wav, m4a, ogg, webm), application/pdf, text/plain, text/csv, text/markdown, application/json.
  • Maximum file size: 400 MB per file. Large screen recordings and audio memos are supported.
  • Files are stored in the team's Supabase Storage bucket and served via signed URLs.
Time Tracking
⏱️
Time Tracking is available on the Team plan only. Log daily QA effort with categories, filter by project and team member, and view daily, weekly, and monthly summaries.
GET /api/time-entries Auth Required

List time tracking entries for the authenticated user's team. Supports filtering by time period, project, and category.

Query Parameters

period optional
string — today, week, month, or all (default: all)
project_id optional
string — Filter by project UUID
category optional
string — Filter by category (e.g. testing, bug-triage, automation)
sort optional
string — newest (default), oldest, most_time, least_time

Response

200 OK
{
  "entries": [
    {
      "id": "uuid",
      "description": "Regression testing checkout flow",
      "category": "testing",
      "duration_minutes": 45,
      "project_id": "uuid",
      "entry_date": "2026-03-22",
      "user_id": "uuid",
      "created_at": "2026-03-22T10:00:00Z",
      "updated_at": "2026-03-22T10:00:00Z"
    }
  ],
  "count": 1
}
POST /api/time-entries Auth Required

Create a new time tracking entry. Log hours spent on QA tasks with categories for team reporting and analytics.

Request Body

description required
string — Description of the work done
category required
string — Category (e.g. testing, bug-triage, automation, review, meeting)
duration_minutes required
integer — Duration in minutes (minimum 1)
project_id optional
string — Associate with a project UUID
entry_date optional
string — Date in YYYY-MM-DD format (defaults to today)

Response

201 Created
{
  "id": "uuid",
  "description": "Regression testing checkout flow",
  "category": "testing",
  "duration_minutes": 45,
  "project_id": "uuid",
  "entry_date": "2026-03-22",
  "created_at": "2026-03-22T10:00:00Z"
}
PATCH /api/time-entries/:id Auth Required

Update an existing time tracking entry. Only provided fields are changed.

Request Body

description optional
string — Updated description
category optional
string — Updated category
duration_minutes optional
integer — Updated duration in minutes
project_id optional
string — Re-associate to a different project
entry_date optional
string — Updated date in YYYY-MM-DD format

Response

200 OK
{
  "id": "uuid",
  "description": "Regression testing checkout flow (updated)",
  "category": "testing",
  "duration_minutes": 60,
  "project_id": "uuid",
  "entry_date": "2026-03-22",
  "updated_at": "2026-03-22T11:00:00Z"
}
DELETE /api/time-entries/:id Auth Required

Delete a time tracking entry. This action cannot be undone.

Response

200 OK
{ "deleted": true }

Notes

  • Time Tracking is a Team plan feature. Free and Pro plan users receive a 403 Forbidden response.
  • Entries are scoped to the authenticated user's team. All team members can view entries; only the entry creator or team admins can update or delete.
Resources (CoPilot recordings)
🔒
Resources are private per user. Each row is strictly scoped by owner_id = auth.uid() — neither the dashboard nor this API ever returns another user's resources. Rows are filtered by team_id (workspace) and optionally project_id. The Chrome extension syncs with these endpoints to mirror local recordings into bugAgent.
GET /api/resources Auth Required

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.

Query Parameters

team_id required
string — Workspace UUID. Caller must be an active member; otherwise 403.
project_id optional
string — Narrow to a single project. Omit to list all projects in the workspace.
since optional
string — ISO 8601 timestamp. Only return resources with updated_at > since. Use for delta polling.

Response

200 OK
{
  "resources": [
    {
      "id": "uuid",
      "owner_id": "uuid",
      "team_id": "uuid",
      "project_id": "uuid",
      "kind": "recording",
      "name": "Recording · Apr 22, 3:45pm",
      "duration_ms": 42300,
      "options": { "actions": true, "playwright": true, "video": false },
      "actions": [ { "type": "click", "target": { ... }, "timestamp": 1200 } ],
      "playwright": "import { test, expect } from '@playwright/test';\n\ntest('...')",
      "stats": { "actionCount": 18, "pageCount": 3 },
      "pages": ["https://example.com/login", "https://example.com/dashboard"],
      "file_path": null,
      "file_size_bytes": null,
      "file_mime_type": null,
      "client_id": "7",
      "created_at": "2026-04-22T19:45:00Z",
      "updated_at": "2026-04-22T19:47:22Z"
    }
  ],
  "server_time": "2026-04-22T19:50:10Z"
}
POST /api/resources Auth Required

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.

Request Body

team_id required
string — Workspace UUID.
project_id optional
string — Project UUID. Null = workspace-level.
kind optional
string — Default "recording". Reserved for future types (video, HAR).
name optional
string — Human-readable label (max 200 chars).
duration_ms optional
number — Recording length in milliseconds.
options optional
object — Capture options frozen at start time (actions, playwright, video, playwrightAssertions, playwrightWaits, playwrightLocators).
actions optional
array — Captured action log (click / fill / navigate / wait / assert / ...).
playwright optional
string — Generated Playwright test source. User-editable via PATCH.
stats optional
object — { actionCount, pageCount }.
pages optional
array<string> — Unique URLs visited.
client_id optional
string — Stable local id (e.g. IndexedDB auto-increment). Used for idempotent retries.

Response

201 Created / 200 OK (deduped)
{
  "resource": { /* same shape as GET /api/resources */ },
  "deduped": true   // only present when matched on client_id and updated
}
GET /api/resources/:id Auth Required

Fetch a single resource by id. Returns 404 if the resource does not exist or belongs to a different user.

PATCH /api/resources/:id Auth Required

Update whitelisted fields of a resource. Owner-only. Immutable: owner_id, team_id, created_at, client_id.

Request Body (any subset)

name
string — New display name (max 200 chars).
playwright
string | null — Replace the generated Playwright script. Pass null to clear.
project_id
string | null — Move the resource to a different project (or workspace-level).
actions
array — Replace the action log.
options, stats, pages
object / array — Replace in place.
DELETE /api/resources/:id Auth Required

Delete a resource. Owner-only. Also removes any associated file in the user-resources storage bucket.

AI Assistant
POST /api/ai/chat Auth Required

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.

Request Body

message required
string — The user's message (max 10,000 characters)
history optional
array — Previous conversation messages. Each entry: {"role": "user"|"assistant", "content": "..."}. Max 40 entries, 100K total characters.

Response

The endpoint content-negotiates the response transport on the Accept header.

200 OK — default (buffered JSON)
{"response": "You have 12 open critical bugs. The most recent is..."}

Send Accept: text/event-stream to receive a Server-Sent Events stream of Anthropic content_block_delta events as Claude generates the response. Useful for live token-by-token rendering. The buffered JSON form remains the default for any caller that doesn't request streaming.

200 OK — streaming (Accept: text/event-stream)
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"You "}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"have 12 "}}

... message_stop event ends the stream

Notes

  • Powered by Claude Sonnet. Responses are professional, concise, and emoji-free.
  • The assistant is scoped to your current workspace's data only.
  • Context-aware: automatically includes custom AI instructions and uploaded knowledge documents (product specs, testing playbooks, etc.) configured in Settings.
  • Supports voice-to-text input via Whisper transcription for long recording sessions (up to 20+ minutes).
  • History role values are validated server-side. Only user and assistant roles are accepted.
  • Off-topic questions (politics, opinions, etc.) are politely redirected.
POST /api/ai/create-report Auth Required

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.

Request Body

title required
string — Bug title (max 500 characters)
description required
string — Full bug description including steps to reproduce, expected/actual behavior (max 50,000 characters)
type optional
string — Report type: ui, performance, crash, security, logic, data, network, accessibility, compatibility, functional, ui-ux, data-integrity, feature-request, enhancement, technical-debt, documentation, devops, ux-improvement, integration. Auto-classified if omitted.
severity optional
string — Prefer formal QA codes: s1 (Blocker), s2 (Critical), s3 (Major, default), s4 (Minor). Legacy values still accepted: critical, high, medium, low.
project_id optional
string — Target project UUID. Must belong to the user's current workspace. Falls back to default project.
internal_notes optional
string — Internal testing notes (max 10,000 characters)
environment optional
object — {"browser": "...", "os": "...", "device": "..."}
sync_to_jira optional
boolean — Automatically create a Jira issue if Jira is connected. Default: false.
attachments optional
array — Up to 10 file attachments. Each: {"filename": "...", "mimeType": "image/png", "data": "base64..."}. Max 400 MB per file. Accepted MIME types: any image/*, video/*, audio/*, application/pdf, text/plain, text/csv, text/markdown, or application/json.

Response

200 OK
{
  "success": true,
  "id": "uuid",
  "title": "Login button unresponsive on mobile",
  "type": "ui",
  "severity": "high",
  "attachments_count": 2,
  "jira_key": "PROJ-42"
}

Security

  • Verifies the authenticated user is a member of the current workspace before creating the report.
  • Project ID is validated against the workspace. Cross-workspace report creation is blocked.
  • Attachment MIME types are restricted to any image, video, audio, PDF, or text/JSON file. Max 400 MB per file.
Developer Notes
POST /api/claude/push Auth Required

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.

Internally runs a multi-step chain that adapts to severity. Medium/low bugs get the three-step chain: Sonnet drafts, a powerful OpenAI model (default gpt-5) critiques the draft as a skeptical peer reviewer, and Sonnet synthesizes the final notes. Critical/high bugs escalate to the five-step debate chain: after the critique, Sonnet writes a point-by-point rebuttal, then a different-model adjudicator (default claude-opus-4-5) reads the full transcript and writes the final notes with independent judgment. Each round is persisted for audit — see the Response + bug_reports column notes below.

Graceful degradation at every step: if OPENAI_API_KEY is missing or the challenger call fails the draft is used as the final answer and critique is null. If the adjudicator call fails the chain falls back to the simple synthesis path. You always get useful notes, never a hard error.

Request Body

report_id required
string — The ID of the bug report to send to Claude for analysis

Response

200 OK
{
  "analysis": "## Probable Root Cause\n...",
  "pushed_at": "2026-03-22T14:30:00Z"
}

Response (additional fields)

draft
string — Initial Sonnet analysis before any review. Persisted as claude_draft_analysis.
critique
string | null — Challenger's review of the draft. null when the challenge step was skipped (missing OPENAI_API_KEY, disabled via DEVNOTES_CHALLENGER_ENABLED=false, or an API error — draft is then used as the final answer).
rebuttal
string | null — Sonnet's point-by-point response to the critique. Only written on the debate path (top-two severity buckets: s1/critical or s2/high). null otherwise or when the debate chain couldn't adjudicate.
challenger_model
string | null — OpenAI model identifier that ran as challenger (e.g. gpt-5). null when the step was skipped.
adjudicator_model
string | null — Model identifier that arbitrated the debate and wrote the final notes (e.g. claude-opus-4-5). null when the debate path didn't run or the adjudicator call failed.
debated
boolean — true if the full debate chain (critique → rebuttal → adjudicator) produced the final notes, false otherwise.

Notes

  • No per-team Claude connection required. The platform ANTHROPIC_API_KEY and OPENAI_API_KEY are used directly.
  • Auto-fires on bug creation (via POST /ai/create-report and the failed-automation auto-bug-creation path). You only need this endpoint for manual regenerate.
  • Stored on the bug record (see GET /reports/:id) as claude_analysis (final), claude_draft_analysis (pre-challenger), claude_challenger_critique (peer review), claude_challenger_model, claude_pushed_at, and claude_status (analyzing / done / failed).
  • Regenerating clears dev_notes_stale (the flag that lights up the "Regenerate?" banner in the dashboard when the description or attachments have changed since the last run).
  • Cost: ~3x a single-shot call (draft + critique + synthesis). Latency: ~10-15s on a fresh report. Auto-fire is fire-and-forget so this doesn't block the UI.
  • Available on Pro, Team, and Enterprise plans.
POST /api/claude/fix-area Auth Required

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.

Request Body

report_id required
string — The UUID of the bug report to analyze. Short IDs (PREFIX-NNN) are NOT accepted here; use GET /reports/:id to resolve one first if needed.

Response

202 Accepted
{
  "status": "analyzing"
}

Resulting fields on bug_reports

likely_fix_area
string | null — Sonnet's plain-text output, typically 3-5 short bullets. Set to an error message when likely_fix_area_status = "failed".
likely_fix_area_status
string | null — analyzing | done | failed. Null when the analysis has never been attempted.
likely_fix_area_generated_at
string (ISO 8601) | null — Timestamp of the most recent write to likely_fix_area.

Notes

  • No claude_connections row is required. The endpoint uses ANTHROPIC_API_KEY on the platform side.
  • Auto-fires from POST /ai/create-report and from the failed-automation auto-bug-creation path. You only need to call this endpoint for manual retry / regenerate.
  • Repo-grounded output requires (a) github_connections.access_token on the team and (b) github_repo on the bug's project. Without either, the output is a general-speculation fallback with a "connect a repo" suggestion in the first bullet.
  • The Dashboard uses Sonnet 4 (claude-sonnet-4-20250514). Costs are on the platform, not the caller's team.
  • Available on Pro, Team, and Enterprise plans (matches the Developer Notes card gating).
bugAgent Tools (SDK)
🎬
For setup guides, SDK installation, and integration details see the bugAgent Tools (SDK) documentation.
POST /api/sessions/capture Auth Required

Submit a session recording captured by the bugAgent SDK. The SDK records the last 60 seconds of user activity — clicks, navigation, errors, and network failures — and sends the data to this endpoint. The AI then analyzes the session to help auto-draft bug reports. Available on Pro, Team, and Enterprise plans.

Request Body

events required
array — Recorded session events. Each event: {"type": "click"|"navigation"|"error"|"network", "timestamp": "ISO8601", "data": {...}}
duration_ms required
number — Duration of the captured session in milliseconds (max 60000)
project_id optional
string — Target project UUID. Falls back to default project.
url optional
string — Page URL where the session was captured
environment optional
object — {"browser": "...", "os": "...", "device": "..."}

Response

201 Created
{
  "id": "uuid",
  "duration_ms": 58200,
  "event_count": 34,
  "created_at": "2026-03-19T14:30:00Z"
}

Notes

  • Requires Pro, Team, or Enterprise plan. Free plan users receive a 403 error.
  • Maximum payload size: 5 MB per session capture.
  • Sessions are retained for 30 days (Pro/Team) or configurable on Enterprise.
GET /api/sessions Auth Required

List SDK sessions for the current workspace. Supports pagination and filtering.

Query Parameters

project_id optional
string — Filter by project
report_id optional
string — Filter sessions attached to a specific bug report
limit optional
number — Results per page (default 20, max 100)
offset optional
number — Pagination offset

Response

200 OK
{
  "sessions": [
    {
      "id": "uuid",
      "duration_ms": 58200,
      "event_count": 34,
      "url": "https://app.example.com/checkout",
      "report_id": null,
      "created_at": "2026-03-19T14:30:00Z"
    }
  ],
  "total": 42
}
GET /api/sessions/:id Auth Required

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.

Response

200 OK
{
  "id": "uuid",
  "duration_ms": 58200,
  "event_count": 34,
  "url": "https://app.example.com/checkout",
  "environment": {"browser": "Chrome 120", "os": "macOS 15"},
  "report_id": null,
  "events": [
    {"type": "click", "timestamp": "2026-03-19T14:29:02Z", "data": {"selector": "#checkout-btn"}},
    {"type": "error", "timestamp": "2026-03-19T14:29:03Z", "data": {"message": "TypeError: Cannot read property..."}}
  ],
  "created_at": "2026-03-19T14:30:00Z"
}
PATCH /api/sessions Auth Required

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.

Request Body

session_id required
string — Session replay UUID
report_id required
string — Bug report UUID to attach the session to

Response

200 OK
{
  "success": true,
  "session_id": "uuid",
  "report_id": "uuid"
}

Notes

  • Both the session and the report must belong to the same workspace.
  • A session can only be attached to one report. Re-attaching overwrites the previous link.
Team Booster
POST /api/team-booster Auth Required

Instantly scale your QA team with booster testers. Accounts are provisioned automatically with tester access. Pro and Team plans only. Free plan returns 403. You will not be charged until approval has been given.

Request Body

ParameterTypeRequiredDescription
team_sizeintegerYesNumber of tester accounts to provision (1–10)
locationstringYesGeographic location for testers (e.g. “US”, “EU”, “APAC”)
durationstringYesDuration of the engagement (e.g. “1 week”, “1 month”)
product_urlstringNoURL of the product to be tested
product_typesstring[]NoTypes of products (e.g. ["web app", "mobile app"])
tech_levelsstring[]NoTechnical levels required (e.g. ["junior", "senior"])
budgetstringYesBudget for the engagement (e.g. “$500”, “$2000/month”)

Response

200 OK
{ "success": true, "created": 5 }

Error Responses

403 Forbidden
{ "error": "Team Booster is available on Pro and Team plans only." }
GET /api/admin/changelog Public

List all changelog entries, most recent first. Also available as an RSS feed.

Response

200 OK
{
  "entries": [
    {
      "id": "uuid",
      "title": "REST API parity with MCP tools",
      "summary": "Added 12 new REST API endpoints...",
      "content": "Detailed markdown content...",
      "tags": ["api", "new-feature"],
      "published_at": "2026-03-17T12:46:28Z"
    },
    ...
  ]
}
Test Cases
GET /api/test-cases Auth Required

List test cases for the authenticated user's team. Supports search, filtering by priority, type, and status, sorting, and pagination.

Query Parameters

search optional
string — Keyword search across test case names and descriptions
priority optional
string — Filter by priority: critical, high, medium, low
type optional
string — Filter by type: functional, regression, smoke, integration, e2e, performance, security, usability, accessibility
status optional
string — Filter by status: active, draft, deprecated
sort optional
string — Sort by: newest, oldest, name, priority (default: newest)
page optional
number — Page number (default: 1)
per_page optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "cases": [
    {
      "id": "uuid",
      "name": "Verify checkout with discount code",
      "description": "Ensure discount codes apply correctly",
      "priority": "high",
      "type": "functional",
      "status": "active",
      "tags": ["checkout", "discounts"],
      "estimated_time": 300,
      "steps_count": 5,
      "project_id": "uuid",
      "created_at": "2026-03-25T10:00:00Z",
      "updated_at": "2026-03-25T10:00:00Z"
    }
  ],
  "total": 42,
  "page": 1,
  "per_page": 20
}
POST /api/test-cases Auth Required

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.

Request Body

name required
string — Test case name (max 200 chars)
description optional
string — Detailed description of the test case
preconditions optional
string — Prerequisites that must be met before executing the test
template_type optional
string — steps (default) or text. Drives which template the dashboard shows on the case detail page. Both fields can be populated regardless; the platform stores both so template flips are non-destructive.
steps optional
array — Array of step objects: { "action": "Click login", "expected": "Login form appears" }. Used when template_type is steps.
text_content optional
string — Free-form description of the steps (multi-line OK; newlines render as line breaks during a test run). Used when template_type is text.
priority optional
string — critical, high, medium (default), low
type optional
string — functional (default), regression, smoke, integration, e2e, performance, security, usability, accessibility
tags optional
array — Array of tag strings for categorization
estimated_time optional
integer — Estimated execution time in seconds
project_id optional
string — Associate with a project UUID. Uses default project if omitted

Response

201 Created
{
  "id": "uuid",
  "name": "Verify checkout with discount code",
  "description": "Ensure discount codes apply correctly",
  "preconditions": "User is logged in with items in cart",
  "steps": [
    { "order": 1, "action": "Navigate to cart", "expected": "Cart page loads with items" },
    { "order": 2, "action": "Enter code SAVE20", "expected": "20% discount applied" },
    { "order": 3, "action": "Click checkout", "expected": "Order total reflects discount" }
  ],
  "priority": "high",
  "type": "functional",
  "status": "active",
  "tags": ["checkout", "discounts"],
  "estimated_time": 300,
  "project_id": "uuid",
  "created_at": "2026-03-25T10:00:00Z",
  "updated_at": "2026-03-25T10:00:00Z"
}
POST /api/test-cases/:id/attachments Auth Required

Upload an attachment file to a test case. Multipart/form-data with a single file field per request. Stored in the test-case-attachments Supabase Storage bucket; metadata appended to the case's attachments jsonb column.

Constraints

  • Max 10 attachments per case (per-case cap, enforced server-side).
  • Max 256 MB per file (per-file cap, enforced both by the API and the storage bucket).
  • Allowed MIME types: image (png/jpeg/gif/webp/svg/heic/avif), application/pdf, doc/docx, xls/xlsx/csv, OpenDocument text/spreadsheet, text/plain, text/markdown, video (mp4/webm/quicktime/avi), audio (mp3/wav/ogg/webm/m4a/mp4).

Response

201 Created
{ "attachment": { "id": "uuid", "filename": "shot.png", "mime_type": "image/png", "size": 12345, "storage_path": "team_id/case_id/file_id_shot.png", "public_url": "https://.../storage/v1/object/public/test-case-attachments/...", "uploaded_at": "...", "uploaded_by": "user_id" }, "attachments_count": 1 }
DELETE /api/test-cases/:id/attachments/:attachment_id Auth Required

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.

Response

200 OK
{ "removed": "attachment_id", "attachments_count": 0 }
GET /api/test-cases/:id Auth Required

Get a test case by ID with its full steps and execution history.

Response

200 OK
{
  "id": "uuid",
  "name": "Verify checkout with discount code",
  "description": "Ensure discount codes apply correctly",
  "preconditions": "User is logged in with items in cart",
  "steps": [
    { "order": 1, "action": "Navigate to cart", "expected": "Cart page loads with items" },
    { "order": 2, "action": "Enter code SAVE20", "expected": "20% discount applied" }
  ],
  "priority": "high",
  "type": "functional",
  "status": "active",
  "tags": ["checkout", "discounts"],
  "estimated_time": 300,
  "project_id": "uuid",
  "history": [
    { "run_id": "uuid", "run_name": "Sprint 12 Regression", "status": "passed", "executed_at": "2026-03-24T14:00:00Z" }
  ],
  "created_at": "2026-03-25T10:00:00Z",
  "updated_at": "2026-03-25T10:00:00Z"
}
PATCH /api/test-cases/:id Auth Required

Update any field of a test case. Only provided fields are changed.

Request Body

name optional
string — Updated name
description optional
string — Updated description
preconditions optional
string — Updated preconditions
template_type optional
string — steps or text. Switches the surface shown on the case detail page. Both steps and text_content stay populated through the switch — the platform stores them independently so flips are non-destructive.
steps optional
array — Replaces all steps with new array
text_content optional
string — Free-form steps text. Used when template_type is text. Multi-line OK; an empty string normalises to null.
priority optional
string — critical, high, medium, low
type optional
string — Test case type
status optional
string — active, draft, deprecated
tags optional
array — Replaces all tags
urls optional
array — Up to 10 URLs associated with the case (max enforced server-side). The dedicated upload endpoints for attachment files land in a follow-up release; this field accepts URL strings today.
estimated_time optional
integer — Estimated execution time in seconds

Response

200 OK
{ "id": "uuid", "name": "Updated name", ... }
DELETE /api/test-cases/:id Auth Required

Permanently delete a test case. Removes it from all suites.

Response

200 OK
{ "success": true }
POST /api/test-cases/duplicate Auth Required

Duplicate an existing test case including all steps, tags, and metadata. The copy is created with the name prefix "[Copy]".

Request Body

case_id required
string — ID of the test case to duplicate

Response

201 Created
{ "id": "uuid", "name": "[Copy] Verify checkout with discount code", ... }
POST /api/test-cases/bulk Auth Required

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.

Request Body

ids required
array — 1–500 test case UUIDs
action required
One of: set_priority, set_status, set_type, set_folder, add_tags, remove_tags, add_to_suite, pin, unpin
params action-dependent
Object whose required keys depend on action:
set_priority: { priority: "critical"|"high"|"medium"|"low" }
set_status: { status: "active"|"draft"|"deprecated" }
set_type: { type: "functional"|...|"exploratory" } (must match the DB CHECK — e2e and other are NOT accepted; use integration or functional)
set_folder: { folder_id: "uuid" | null } (null unfiles)
add_tags/remove_tags: { tags: ["..."] }
add_to_suite: { suite_id: "uuid" }
pin/unpin: no params

Response

200 OK
{ "applied": 47, "skipped": 3, "errors": [] }
GET /api/test-cases/review-candidates Auth Required

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.

Response

200 OK
{
  "never_run":      [ { "id": "uuid", "name": "...", "reason": "no runs in 90+ days since creation" } ],
  "always_passes":  [ { "id": "uuid", "name": "...", "reason": "5+ runs in last 90d, all passed" } ],
  "always_skipped": [ { "id": "uuid", "name": "...", "reason": "3+ runs in last 90d, all skipped" } ],
  "total": 12
}
POST /api/test-cases/import/figma/request Auth Required

Step 1 of the 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.

Request Body

{
  "project_id": "uuid | null",
  "folder_id": "uuid | null",          // pick existing
  "new_folder_name": "string | null",  // or create one
  "project_context": "string",         // up to 2000 chars
  "depth": "quick | thorough",
  "file_name": "designs.zip",
  "file_size_bytes": 11345678
}

Response

200 OK
{
  "job_id": "uuid",
  "folder_id": "uuid | null",
  "upload": { "path": "...", "token": "...", "signed_url": "https://..." }
}
POST /api/test-cases/import/figma/start Auth Required

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.

Request Body

{ "job_id": "uuid" }

Response

202 Accepted
{ "ok": true, "job_id": "uuid" }
GET /api/test-cases/import/figma/:id Auth Required

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.

Response

200 OK
{
  "job": {
    "id": "uuid",
    "status": "queued | uploading | processing | completed | failed | cancelled",
    "progress_phase": "classifying",
    "progress_current": 7,
    "progress_total": 12,
    "progress_message": "Classifying 12 unique frames",
    "total_frames": 34,
    "unique_frames": 12,
    "test_cases_created": 0,
    "cost_cents": 0,
    "error": null,
    "file_name": "designs.zip",
    "started_at": "2026-04-20T20:15:00Z",
    "completed_at": null,
    "created_at": "2026-04-20T20:14:48Z"
  }
}
Test Suites
GET /api/test-suites Auth Required

List test suites for the authenticated user's team. Returns each suite with its case count and last run status.

Query Parameters

search optional
string — Keyword search across suite names
page optional
number — Page number (default: 1)
per_page optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "suites": [
    {
      "id": "uuid",
      "name": "Checkout Regression Suite",
      "description": "All checkout-related test cases",
      "case_count": 12,
      "last_run_status": "passed",
      "project_id": "uuid",
      "created_at": "2026-03-25T10:00:00Z"
    }
  ],
  "total": 8
}
POST /api/test-suites Auth Required

Create a new test suite to group related test cases.

Request Body

name required
string — Suite name (max 200 chars)
description optional
string — Suite description
project_id optional
string — Associate with a project UUID

Response

201 Created
{
  "id": "uuid",
  "name": "Checkout Regression Suite",
  "description": "All checkout-related test cases",
  "case_count": 0,
  "project_id": "uuid",
  "project_slug": "checkout-team",
  "created_at": "2026-03-25T10:00:00Z"
}
GET /api/test-suites/:id Auth Required

Get a test suite by ID with its full list of cases in order.

Query Parameters

include_descendants optional
Set to 1 to also return metadata for every descendant sub-suite plus deduped case counts. When enabled, the response includes descendant_suites[], descendant_case_count (cases in sub-suites that aren’t already in this suite), and total_case_count. Useful for run-creation previews.

Response

200 OK
{
  "id": "uuid",
  "name": "Checkout Regression Suite",
  "description": "All checkout-related test cases",
  "cases": [
    { "id": "uuid", "name": "Verify checkout with discount code", "priority": "high", "type": "functional", "order": 1 }
  ],
  "project_id": "uuid",
  "created_at": "2026-03-25T10:00:00Z"
}
PATCH /api/test-suites/:id Auth Required

Update a test suite's name or description.

Request Body

name optional
string — Updated name
description optional
string — Updated description

Response

200 OK
{ "id": "uuid", "name": "Updated Suite Name", ... }
DELETE /api/test-suites/:id Auth Required

Delete a test suite. Test cases within the suite are not deleted.

Response

200 OK
{ "success": true }
POST /api/test-suites/:id/cases Auth Required

Add one or more test cases to a suite.

Request Body

case_ids required
array — Array of test case UUIDs to add

Response

200 OK
{ "added": 3, "case_count": 15 }
DELETE /api/test-suites/:id/cases Auth Required

Remove a test case from a suite.

Request Body

case_id required
string — Test case UUID to remove

Response

200 OK
{ "success": true, "case_count": 14 }
PATCH /api/test-suites/:id/reorder Auth Required

Reorder the test cases within a suite. Provide the full ordered list of case IDs.

Request Body

ordered_case_ids required
array — Array of test case UUIDs in desired order

Response

200 OK
{ "success": true }
PATCH /api/test-suites/reorder Auth Required

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.

Request Body

parent_suite_id required
uuid | null — The shared parent of every suite being reordered. Pass null for root-level suites.
ordered_suite_ids required
array — Up to 200 suite UUIDs in the new desired order. sort_order is rewritten as the array index for each.

Response

200 OK
{ "success": true, "count": 5 }
Test Case Folders
GET /api/test-case-folders Auth Required

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.

Query Parameters

project optional
uuid — Filter to folders for a specific project
parent optional
uuid | root — Direct children of the given folder, or top-level folders when set to root

Response

200 OK
{
  "test_case_folders": [
    {
      "id": "uuid",
      "name": "Smoke Tests",
      "description": "",
      "parent_folder_id": null,
      "depth": 0,
      "sort_order": 0,
      "project_id": "uuid",
      "case_count": 12,
      "created_at": "2026-04-19T...",
      "updated_at": "2026-04-19T..."
    }
  ],
  "total": 1
}
POST /api/test-case-folders Auth Required

Create a new folder. Pass parent_folder_id to nest it; folders can nest up to 3 levels deep.

Request Body

name required
string — Folder name (max 120 chars)
description optional
string
parent_folder_id optional
uuid — Parent folder; folder will be a top-level folder if omitted
project_id optional
uuid — Project association
PATCH /api/test-case-folders/reorder Auth Required

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.

Request Body

parent_folder_id required
uuid | null — The shared parent of every folder being reordered. Pass null for root-level folders.
ordered_folder_ids required
array — Up to 200 folder UUIDs in the new desired order. sort_order is rewritten as the array index for each.

Response

200 OK
{ "success": true, "count": 5 }
Test Runs
GET /api/test-runs Auth Required

List test runs for the authenticated user's team. Returns each run with suite name, assignee, and pass/fail summary.

Query Parameters

search optional
string — Keyword search across run names
status optional
string — Filter by status: in_progress, completed
suite_id optional
string — Filter by test suite UUID
page optional
number — Page number (default: 1)
per_page optional
number — Results per page (default: 20, max: 100)

Response

200 OK
{
  "runs": [
    {
      "id": "uuid",
      "name": "Sprint 12 Regression",
      "suite_id": "uuid",
      "suite_name": "Checkout Regression Suite",
      "status": "in_progress",
      "assigned_to": "uuid",
      "assignee_name": "Jane Smith",
      "summary": { "total": 12, "passed": 8, "failed": 2, "blocked": 1, "skipped": 0, "untested": 1 },
      "created_at": "2026-03-25T10:00:00Z"
    }
  ],
  "total": 5
}
POST /api/test-runs Auth Required

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.

If any cases have a per-case assignee, an in-app bell notification AND a digest email are sent to each assignee. Emails respect the per-user opt-out at notification_preferences.email_test_case_assignment (default on); the in-app bell is always shown.

Request Body

suite_id required
string — Test suite UUID to create the run from (the run includes cases from this suite and every sub-suite underneath it)
name required
string — Run name (e.g. "Sprint 12 Regression")
assigned_to optional
string — User UUID of the assignee

Response

201 Created
{
  "id": "uuid",
  "name": "Sprint 12 Regression",
  "suite_id": "uuid",
  "status": "in_progress",
  "assigned_to": "uuid",
  "results": [
    { "case_id": "uuid", "case_name": "Verify checkout with discount code", "status": "untested" }
  ],
  "created_at": "2026-03-25T10:00:00Z"
}
GET /api/test-runs/:id Auth Required

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.

Response

200 OK
{
  "id": "uuid",
  "name": "Sprint 12 Regression",
  "suite_id": "uuid",
  "suite_name": "Checkout Regression Suite",
  "status": "in_progress",
  "assigned_to": "uuid",
  "assignee_name": "Jane Smith",
  "results": [
    {
      "case_id": "uuid",
      "case_name": "Verify checkout with discount code",
      "status": "passed",
      "actual_result": "Discount applied correctly",
      "notes": "",
      "executed_at": "2026-03-25T11:00:00Z"
    }
  ],
  "summary": { "total": 12, "passed": 8, "failed": 2, "blocked": 1, "skipped": 0, "untested": 1 },
  "created_at": "2026-03-25T10:00:00Z"
}
PATCH /api/test-runs/:id Auth Required

Update a test run's name, assignee, or status.

Request Body

name optional
string — Updated run name
description optional
string — Updated description
assigned_to optional
string — Updated assignee UUID
status optional
string — Updated status. Setting archived on a run that is already archived short-circuits to a no-op 200 { id, status: "archived", already_archived: true } so retries are idempotent. The dashboard UI hides the Archive Run button on archived runs so this path is only reachable from MCP / scripts / direct API callers.

Response

200 OK
{ "id": "uuid", "name": "Updated Run Name", ... }
POST /api/test-runs/:id/results Auth Required

Save the execution result for a specific test case in a run. Mark each case as passed, failed, blocked, or skipped.

Request Body

case_id required
string — Test case UUID
status required
string — passed, failed, blocked, skipped
actual_result optional
string — What actually happened during execution
notes optional
string — Free-form tester notes captured during execution. Persisted on test_run_results.notes and surfaced inline on the run carousel; never synced to Jira.

Response

200 OK
{ "success": true, "status": "failed", "case_id": "uuid", "notes": "..." }
PATCH /api/test-runs/:id/results Auth Required

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.

Request Body

case_id required
string — Test case UUID (must already belong to this run)
assigned_to required
string | null — User UUID to assign, or null to unassign

Response

200 OK
{ "success": true, "case_id": "uuid", "assigned_to": "uuid-or-null" }
POST /api/test-runs/:id/results/attachments Auth Required

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.

Request

Multipart form data:

case_id required
string — Test case UUID (must already be part of this run with a saved status)
files required
File[] — Up to 8 files per request, 25 MB per file, 100 MB total. Accepts image, video, audio, plain text, markdown, JSON, CSV, PDF.

Response

200 OK
{
  "success": true,
  "attachments": [
    {
      "id": "uuid",
      "storage_path": "team-id/test-runs/run-id/case-id/uuid_screenshot.png",
      "filename": "screenshot.png",
      "url": "https://....supabase.co/storage/v1/.../screenshot.png",
      "type": "image",
      "mimeType": "image/png",
      "size": 86552,
      "uploaded_at": "2026-05-09T14:00:00Z",
      "uploaded_by": "user-uuid"
    }
  ],
  "added": [ /* just the new attachments from this request */ ]
}
DELETE /api/test-runs/:id/results/attachments Auth Required

Remove a single attachment from a test case result. Deletes both the file in storage and the metadata entry. Returns the updated attachments list.

Request Body

case_id required
string — Test case UUID
attachment_id required
string — Attachment UUID (the id returned from the POST)

Response

200 OK
{ "success": true, "attachments": [ /* remaining attachments */ ] }
POST /api/test-runs/:id/complete Auth Required

Mark a test run as completed. Calculates and stores the final pass rate.

Response

200 OK
{
  "id": "uuid",
  "status": "completed",
  "summary": { "total": 12, "passed": 10, "failed": 2, "blocked": 0, "skipped": 0, "untested": 0 },
  "pass_rate": 83.3,
  "completed_at": "2026-03-25T15:00:00Z"
}
POST /api/test-runs/:id/rerun-failed Auth Required

Create a new test run containing only the failed cases from a completed run. Useful for re-testing after fixes.

Response

201 Created
{
  "id": "uuid",
  "name": "Re-run: Sprint 12 Regression (failed)",
  "suite_id": "uuid",
  "status": "in_progress",
  "results": [
    { "case_id": "uuid", "case_name": "Apply expired discount code", "status": "untested" }
  ],
  "created_at": "2026-03-25T16:00:00Z"
}
Test Reports
GET /api/test-reports Auth Required

Get completed test run reports with pass rate and date range filtering. Returns aggregated test execution data.

Query Parameters

from optional
string — ISO 8601 date. Only return runs completed on or after this date
to optional
string — ISO 8601 date. Only return runs completed on or before this date
suite_id optional
string — Filter by test suite UUID

Response

200 OK
{
  "reports": [
    {
      "run_id": "uuid",
      "run_name": "Sprint 12 Regression",
      "suite_name": "Checkout Regression Suite",
      "status": "completed",
      "pass_rate": 83.3,
      "summary": { "total": 12, "passed": 10, "failed": 2, "blocked": 0, "skipped": 0 },
      "completed_at": "2026-03-25T15:00:00Z"
    }
  ],
  "total": 15,
  "average_pass_rate": 87.5
}
GET /api/test-reports/overview Auth Required

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.

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago)
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict to a single project
suite optional
uuid — restrict to a suite + all its descendants (via get_suite_descendants)

Response

200 OK
{
  "range": { "from": "...", "to": "...", "previous_from": "...", "previous_to": "..." },
  "kpis": {
    "pass_rate":                { "current": 82, "previous": 78, "delta": 4 },
    "runs_completed":           { "current": 12, "previous": 8,  "delta": 4 },
    "avg_run_duration_seconds": { "current": 1840, "previous": 2100, "delta": -260 },
    "cases_executed":           { "current": 245, "previous": 198, "delta": 47 }
  },
  "trend": {
    "buckets": ["2026-04-06", "2026-04-13", "2026-04-20"],
    "pass_rate":      [78, 80, 82],
    "runs":           [3, 4, 5],
    "cases_executed": [50, 65, 70]
  },
  "capped": false
}
GET /api/test-reports/failures Auth Required

Failure analysis for the Reports tab — four parallel "what to fix this week?" lists.

  • flaky_cases — cases that flip between pass/fail in the period (sorted by flip count)
  • failing_cases — pass rate < 50% with at least 3 runs (noise filter)
  • failing_suites — same logic at suite level via test_run_results.suite_id
  • regressed_cases — most-recent execution failed but the period contained an earlier pass

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago)
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict to a single project
suite optional
uuid — restrict to a suite + all its descendants

Response

200 OK
{
  "flaky_cases": [
    { "id": "uuid", "name": "Login redirects to dashboard",
      "suite_name": "Auth", "flips": 4,
      "pass_count": 3, "fail_count": 5, "total_runs": 8 }
  ],
  "failing_cases": [
    { "id": "uuid", "name": "Coupon stacks past expiry",
      "suite_name": "Billing/Checkout", "fail_rate": 67,
      "fail_count": 4, "total_runs": 6, "last_failed_at": "..." }
  ],
  "failing_suites": [
    { "id": "uuid", "name": "Billing", "fail_rate": 32,
      "fail_count": 11, "total_runs": 34 }
  ],
  "regressed_cases": [
    { "id": "uuid", "name": "...", "suite_name": "...",
      "last_failed_at": "...",
      "pass_count": 2, "fail_count": 1, "total_runs": 3 }
  ]
}
GET /api/test-reports/suite-health Auth Required

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.

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago)
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict to a single project
suite optional
uuid — restrict to a suite + all its descendants (via get_suite_descendants)

Response

200 OK
{
  "suites": [
    {
      "id": "uuid",
      "name": "Billing",
      "depth": 0,
      "parent_suite_id": null,
      "pass_rate": 71,
      "pass_rate_previous": 84,
      "trend": "down",                  // up | down | flat | new
      "runs_completed": 8,
      "cases_executed": 96,
      "fail_count": 28,
      "last_run_at": "2026-04-20T...",
      "open_bugs": 4
    }
  ]
}

Notes

  • Suite attribution uses test_run_results.suite_id (added in migration 106), which records the originating sub-suite per result — exact, not a lossy join through test_suite_cases.
  • Trend compares the current pass rate to the same metric over the prior equivalent-length window. up / down require a ±3% change; smaller deltas are flat. new means the suite had no runs in the prior window.
  • Open bugs are bug_reports linked from any result in the period whose status is not closed or resolved.
GET /api/test-reports/coverage Auth Required

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.

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago). Used for "covered in period" / "untouched in period" KPIs.
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict the active-case set to a single project
suite optional
uuid — restrict to cases linked via test_suite_cases to the suite + descendants (via get_suite_descendants)

Response

200 OK
{
  "range": { "from": "...", "to": "..." },
  "kpis": {
    "total_active_cases": 250,
    "covered_cases": 195,           // ≥1 execution in [from,to]
    "coverage_pct": 78,
    "untouched_in_period": 55,
    "never_run": 12                 // no execution EVER, not just in period
  },
  "buckets": [
    { "label": "Never run",            "key": "never", "count": 12 },
    { "label": "Last 7 days",          "key": "lt7",   "count": 120 },
    { "label": "8–30 days ago",        "key": "lt30",  "count": 75 },
    { "label": "31–90 days ago",       "key": "lt90",  "count": 27 },
    { "label": "91–180 days ago",      "key": "lt180", "count": 10 },
    { "label": "Older than 180 days",  "key": "gt180", "count": 6 }
  ],
  "suite_coverage": [
    {
      "id": "uuid",
      "name": "Billing/Checkout",
      "depth": 1,
      "total_cases": 18,
      "covered_cases": 4,
      "coverage_pct": 22,
      "stale_count": 14,
      "last_activity_at": "..."
    }
  ]
}

Definitions

  • Active = test_cases.status = 'active'. Drafts (work in progress) and deprecated (intentionally retired) cases are excluded.
  • Covered in period = at least one test_run_results row with executed_at in [from, to] and status not untested.
  • Never run = no test_run_results row in the entire history (catalog hygiene metric, not period-scoped).
  • Bucket order: rows are evaluated in array order, first match wins — so the never bucket is checked before any numeric range.
  • suite_coverage uses cases linked to the suite via test_suite_cases (the test-plan relationship), not test_cases.folder_id (the organizational hierarchy). A case can appear under multiple suites; it counts once per suite. Sorted by coverage_pct ascending; capped at 25 entries.
GET /api/test-reports/tester-productivity Auth Required

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).

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago)
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict to a single project
suite optional
uuid — restrict to a suite + all its descendants (via get_suite_descendants)

Response

200 OK
{
  "range": { "from": "...", "to": "..." },
  "totals": {
    "active_testers": 5,            // testers with ≥1 execution
    "cases_executed": 450,
    "bugs_filed": 12                // distinct bugs linked from results
  },
  "testers": [
    {
      "id": "uuid",
      "name": "Jason H.",
      "email": "jason@...",
      "cases_executed": 145,
      "passed": 120, "failed": 18, "blocked": 4, "skipped": 3,
      "pass_rate": 83,                  // INFORMATIONAL — see notes
      "avg_duration_seconds": 240,
      "runs_assigned": 8,
      "runs_assigned_completed": 6,
      "bugs_filed": 4,
      "last_active_at": "..."
    }
  ]
}

Definitions & caveats

  • cases_executed = test_run_results with this tester_id, executed in [from, to], status not untested.
  • avg_duration_seconds excludes any per-case duration above 24h to defend against forgotten timers skewing the average. Result rows with no recorded duration are omitted from the average entirely (not counted as zero).
  • runs_assigned = test_runs with assigned_to = this user AND created_at in the period. runs_assigned_completed = those whose status is completed.
  • bugs_filed counts only bug reports linked from results this tester executed (test_run_results.bug_report_id). Standalone bug reports filed outside test runs (Bugs tab, MCP, browser extension) are NOT counted — this metric is about testing output, not bug filing in general.
  • pass_rate per tester is informational only. It mostly reflects which cases that tester was assigned. Treat it as a secondary signal, not a quality leaderboard.
  • Capped at 50 testers; results with no tester_id attribution are excluded.
GET /api/test-reports/export.pdf Auth Required

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.

Query Parameters

from optional
YYYY-MM-DD — inclusive window start (default: 30 days ago)
to optional
YYYY-MM-DD — inclusive window end (default: today)
project optional
uuid — restrict to a single project; project name appears in the report header
suite optional
uuid — restrict to a suite + descendants; suite name appears in the report header

Response

A PDF binary with Content-Type: application/pdf and a Content-Disposition: attachment header. The filename includes the date range so saved files stay self-describing: bugagent-qa-report-2026-03-21_2026-04-20.pdf.

Page contents

  • Page 1 — At a glance: 4 KPI tiles (pass rate / runs / avg duration / cases executed) with deltas vs the prior equivalent-length window, a pass-rate trend line chart, and a coverage summary (KPIs + staleness distribution bar).
  • Page 2 — What to fix: top 5 failing cases (≥50% fail rate), top 5 flaky cases (most pass↔fail flips), top 5 recently regressed cases.
  • Page 3 — By suite + by tester: top 12 suites by health (worst pass rate first), top 10 testers by cases run.
Geo-Snap
GET /api/geo-snap Auth Required

List saved Geo-Snap screenshots. Filter by country, search by URL, and paginate results.

Query Parameters

search optional
string — Search by URL substring
country optional
string — Filter by country code (e.g. US, DE, JP)
sort optional
string — Sort order: newest (default), oldest
page optional
integer — Page number (default: 1)
per_page optional
integer — Results per page (default: 20, max: 100)

Response

200 OK
{
  "snaps": [
    {
      "id": "uuid",
      "url": "https://example.com",
      "country": "US",
      "screenshot_url": "https://storage.bugagent.com/geo-snaps/uuid-us.png",
      "status": "completed",
      "created_at": "2026-03-25T10:00:00Z"
    }
  ],
  "total": 15,
  "page": 1,
  "per_page": 20
}
POST /api/geo-snap Auth Required

Capture screenshots of a URL from one or more countries. Free plan: 1 country per request, 10 saved screenshots. Pro/Team: up to 5 countries per request, unlimited saved screenshots.

Request Body

url required
string — The URL to capture (must be a valid public URL)
countries required
string[] — Array of country codes (e.g. ["US", "DE", "JP"]). Free: max 1, Pro/Team: max 5

Response

201 Created
{
  "snaps": [
    {
      "id": "uuid",
      "url": "https://example.com",
      "country": "US",
      "screenshot_url": "https://storage.bugagent.com/geo-snaps/uuid-us.png",
      "status": "completed",
      "created_at": "2026-03-25T10:00:00Z"
    },
    {
      "id": "uuid",
      "url": "https://example.com",
      "country": "DE",
      "screenshot_url": "https://storage.bugagent.com/geo-snaps/uuid-de.png",
      "status": "completed",
      "created_at": "2026-03-25T10:00:00Z"
    }
  ]
}
DELETE /api/geo-snap/:id Auth Required

Delete a saved Geo-Snap screenshot by ID.

Path Parameters

id required
string — The UUID of the Geo-Snap screenshot to delete

Response

200 OK
{ "deleted": true }
Mobile Testing
GET /api/mobile/apps Auth Required

List uploaded mobile apps. Filter by platform, search by name, and paginate results.

Query Parameters

search optional
string — Search by app name
platform optional
string — Filter by platform: android or ios
sort optional
string — Sort order: newest (default), oldest, name
page optional
integer — Page number (default: 1)
per_page optional
integer — Results per page (default: 20, max: 100)

Response

200 OK
{
  "apps": [
    {
      "id": "uuid",
      "name": "MyApp",
      "platform": "android",
      "package_name": "com.example.myapp",
      "version": "2.1.0",
      "file_url": "https://storage.bugagent.com/apps/uuid.apk",
      "file_size": 48500000,
      "automation_count": 3,
      "created_at": "2026-03-20T10:00:00Z"
    }
  ],
  "total": 1
}
POST /api/mobile/apps Auth Required

Upload an APK (Android) or IPA (iOS) app binary. Uses multipart/form-data. Max file size: 500 MB.

Request Body (multipart/form-data)

file required
file — The APK or IPA binary (max 500 MB)
name required
string — Display name for the app
platform required
string — android or ios
version optional
string — App version string (e.g. 2.1.0)
package_name optional
string — Package identifier (e.g. com.example.myapp)

Response

201 Created
{
  "id": "uuid",
  "name": "MyApp",
  "platform": "android",
  "package_name": "com.example.myapp",
  "version": "2.1.0",
  "file_url": "https://storage.bugagent.com/apps/uuid.apk",
  "file_size": 48500000,
  "created_at": "2026-03-20T10:00:00Z"
}
GET /api/mobile/apps/:id Auth Required

Get full details for a mobile app, including automation count.

Path Parameters

id required
string — The UUID of the mobile app

Response

200 OK
{
  "id": "uuid",
  "name": "MyApp",
  "platform": "android",
  "package_name": "com.example.myapp",
  "version": "2.1.0",
  "file_url": "https://storage.bugagent.com/apps/uuid.apk",
  "file_size": 48500000,
  "automation_count": 3,
  "created_at": "2026-03-20T10:00:00Z",
  "updated_at": "2026-03-20T10:00:00Z"
}
PATCH /api/mobile/apps/:id Auth Required

Update an uploaded mobile app's metadata.

Path Parameters

id required
string — The UUID of the mobile app

Request Body

name optional
string — Updated display name
version optional
string — Updated version string
package_name optional
string — Updated package identifier
simulator_file_url optional
string — URL to a simulator .app build (iOS only). Enables in-browser test recording. Set to null to remove.
simulator_storage_path optional
string — Storage path for the simulator build file

Response

200 OK
{
  "id": "uuid",
  "name": "MyApp",
  "platform": "android",
  "package_name": "com.example.myapp",
  "version": "2.1.0",
  "file_url": "https://storage.bugagent.com/apps/uuid.apk",
  "file_size": 48500000,
  "automation_count": 3,
  "created_at": "2026-03-20T10:00:00Z",
  "updated_at": "2026-03-20T10:00:00Z"
}
PUT /api/mobile/apps/:id Auth Required

Replace an app's binary with a new version. Deletes the old file from storage, clears cached platform URLs (forcing re-upload on next run), and updates the team's storage quota. All linked automations and schedules will automatically use the new version. For iOS apps, the simulator build is also removed — re-upload it to continue recording.

Path Parameters

id required
string — The UUID of the mobile app to update

Request Body

storage_path required
string — The new file's storage path (from upload-url signed URL flow)
file_size optional
number — File size in bytes
version optional
string — New version string

Response

200 OK
{ "id": "uuid", "name": "My App", "platform": "android", "version": "2.0.0", "file_size": 15728640, "updated_at": "2026-03-29T..." }
DELETE /api/mobile/apps/:id Auth Required

Delete an uploaded mobile app and its storage file. Automations referencing this app will be orphaned.

Path Parameters

id required
string — The UUID of the mobile app to delete

Response

200 OK
{ "success": true }
GET /api/mobile/automations Auth Required

List mobile automation scripts. Filter by app, script type, and status.

Query Parameters

search optional
string — Search by automation name
app_id optional
string — Filter by mobile app UUID
script_type optional
string — Filter by type: maestro (YAML), appium (Python), or appium_js (JavaScript)
status optional
string — Filter by status: draft, active, paused
sort optional
string — Sort: newest (default), oldest, name

Response

200 OK
{
  "automations": [
    {
      "id": "uuid",
      "name": "Login Flow Test",
      "app_id": "uuid",
      "script_type": "maestro",
      "status": "active",
      "target_devices": [
        "Pixel 7",
        "Samsung Galaxy S23"
      ],
      "created_at": "2026-03-21T09:00:00Z"
    }
  ],
  "total": 1
}
POST /api/mobile/automations Auth Required

Create a mobile automation script. Use Appium for complex cross-platform interactions or Maestro YAML for simple flows.

Request Body

name required
string — Automation name
app_id required
string — UUID of the uploaded mobile app
script_type required
string — maestro (YAML), appium (Appium Python), or appium_js (Appium JS)
script required
string — The test script content (Appium Java/Python or Maestro YAML)
target_devices optional
string[] — Array of target device names (e.g. ["Pixel 7", "Samsung Galaxy S23"])

Response

201 Created
{
  "id": "uuid",
  "name": "Login Flow Test",
  "app_id": "uuid",
  "script_type": "maestro",
  "script": "appId: com.example.myapp\n---\n- launchApp\n- tapOn: \"Login\"\n- inputText: \"[email protected]\"",
  "status": "draft",
  "target_devices": [
    "Pixel 7",
    "Samsung Galaxy S23"
  ],
  "created_at": "2026-03-21T09:00:00Z"
}
GET /api/mobile/automations/:id Auth Required

Get full details for a mobile automation, including app info and recent runs.

Path Parameters

id required
string — The UUID of the automation

Response

200 OK
{
  "id": "uuid",
  "name": "Login Flow Test",
  "app_id": "uuid",
  "app_name": "MyApp",
  "script_type": "maestro",
  "script": "appId: com.example.myapp\n---\n- launchApp\n- tapOn: \"Login\"",
  "status": "active",
  "target_devices": [
    "Pixel 7",
    "Samsung Galaxy S23"
  ],
  "recent_runs": [
    {
      "id": "uuid",
      "status": "passed",
      "device": "Pixel 7",
      "duration_ms": 45000,
      "created_at": "2026-03-22T08:00:00Z"
    }
  ],
  "created_at": "2026-03-21T09:00:00Z"
}
PATCH /api/mobile/automations/:id Auth Required

Update a mobile automation script or its metadata.

Path Parameters

id required
string — The UUID of the automation

Request Body

name optional
string — Updated name
script optional
string — Updated script content
status optional
string — draft, active, or paused
target_devices optional
string[] — Updated target device list

Response

200 OK
{
  "id": "uuid",
  "name": "Login Flow Test",
  "app_id": "uuid",
  "app_name": "MyApp",
  "script_type": "maestro",
  "script": "appId: com.example.myapp\n---\n- launchApp\n- tapOn: \"Login\"",
  "status": "active",
  "target_devices": [
    "Pixel 7",
    "Samsung Galaxy S23"
  ],
  "recent_runs": [
    {
      "id": "uuid",
      "status": "passed",
      "device": "Pixel 7",
      "duration_ms": 45000,
      "created_at": "2026-03-22T08:00:00Z"
    }
  ],
  "created_at": "2026-03-21T09:00:00Z"
}
DELETE /api/mobile/automations/:id Auth Required

Delete a mobile automation. Fails if the automation has active schedules — delete schedules first.

Path Parameters

id required
string — The UUID of the automation to delete

Response

200 OK
{ "success": true }
GET /api/mobile/runs Auth Required

List mobile test runs. Filter by automation and status.

Query Parameters

automation_id optional
string — Filter by automation UUID
status optional
string — Filter: queued, running, passed, failed, errored
sort optional
string — Sort: newest (default), oldest

Response

200 OK
{
  "runs": [
    {
      "id": "uuid",
      "automation_id": "uuid",
      "status": "passed",
      "device": "Pixel 7",
      "os_version": "14",
      "duration_ms": 45000,
      "created_at": "2026-03-22T08:00:00Z"
    }
  ],
  "total": 1
}
POST /api/mobile/runs Auth Required

Trigger a mobile test run. Appium scripts are routed to BrowserStack; Maestro scripts are routed to Maestro Cloud.

Request Body

automation_id required
string — UUID of the mobile automation to run
device required
string — Target device name (e.g. Pixel 7, iPhone 15 Pro)
os_version optional
string — OS version to test on (e.g. 14 for Android 14, 17.2 for iOS)

Response

201 Created
{
  "id": "uuid",
  "automation_id": "uuid",
  "status": "queued",
  "device": "Pixel 7",
  "os_version": "14",
  "provider": "browserstack",
  "created_at": "2026-03-22T08:00:00Z"
}
GET /api/mobile/runs/:id Auth Required

Get full results for a mobile test run, including video recording, Appium logs, device logs, network logs, and duration.

Path Parameters

id required
string — The UUID of the run

Response

200 OK
{
  "id": "uuid",
  "automation_id": "uuid",
  "status": "passed",
  "device": "Pixel 7",
  "os_version": "14",
  "provider": "browserstack",
  "duration_ms": 45000,
  "video_url": "https://storage.bugagent.com/runs/uuid/video.mp4",
  "screenshots": [
    "https://storage.bugagent.com/runs/uuid/screen_001.png"
  ],
  "logs": "https://storage.bugagent.com/runs/uuid/device.log",
  "performance": {
    "cpu_avg": 23.5,
    "memory_peak_mb": 256,
    "battery_drain": 2.1,
    "fps_avg": 58.3
  },
  "created_at": "2026-03-22T08:00:00Z",
  "completed_at": "2026-03-22T08:00:45Z"
}
PATCH /api/mobile/runs/:id Auth Required

Archive a completed mobile test run. Archived runs are hidden from the default listing but can be viewed with the status=archived filter.

Request Body

status required
string — Set to "archived"
GET /api/mobile/devices Auth Required

List available BrowserStack real devices for mobile testing. Optionally filter by platform.

Query Parameters

platform
string — Filter by "android" or "ios". Omit to get both.
GET /api/automations/schedules Auth Required

List all web automation schedules for the team. Returns cron expression, timezone, device, notification settings, and next run time.

POST /api/automations/schedules Auth Required

Create a web automation schedule. Requires automation_id and cron_expression. Optional: timezone, device (desktop, iphone-15, galaxy-s23, etc. — Virtual mode viewport emulation), notify_on_fail (none/email/slack/both), notify_email, slack_channel_id. 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.

GET /api/mobile/schedules Auth Required

List scheduled mobile test runs with cron expression, timezone, and notification settings.

Response

200 OK
{
  "schedules": [
    {
      "id": "uuid",
      "automation_id": "uuid",
      "automation_name": "Login Flow Test",
      "cron_expression": "0 9 * * 1-5",
      "timezone": "America/New_York",
      "devices": [
        "Pixel 7",
        "iPhone 15 Pro"
      ],
      "notify_on_fail": "slack",
      "enabled": true,
      "next_run_at": "2026-03-23T09:00:00Z"
    }
  ],
  "total": 1
}
POST /api/mobile/schedules Auth Required

Create a scheduled mobile test run. Runs are dispatched to the configured devices on the cron schedule.

Request Body

automation_id required
string — UUID of the mobile automation
cron_expression required
string — 5-field cron expression (e.g. 0 9 * * 1-5 for weekdays at 9am)
timezone optional
string — IANA timezone (default: UTC)
devices required
string[] — Array of device names to run on each schedule trigger
notify_on_fail optional
string — Notification on failure: none (default), email, slack, both

Response

201 Created
{
  "id": "uuid",
  "automation_id": "uuid",
  "cron_expression": "0 9 * * 1-5",
  "timezone": "America/New_York",
  "devices": [
    "Pixel 7",
    "iPhone 15 Pro"
  ],
  "notify_on_fail": "slack",
  "enabled": true,
  "next_run_at": "2026-03-23T09:00:00Z",
  "created_at": "2026-03-22T10:00:00Z"
}
PATCH /api/mobile/schedules/:id Auth Required

Update a mobile test schedule.

Path Parameters

id required
string — The UUID of the schedule

Request Body

cron_expression optional
string — Updated cron expression
timezone optional
string — Updated timezone
devices optional
string[] — Updated device list
notify_on_fail optional
string — Updated notification setting
enabled optional
boolean — Enable or disable the schedule

Response

200 OK
{
  "id": "uuid",
  "automation_id": "uuid",
  "cron_expression": "0 9 * * 1-5",
  "timezone": "America/New_York",
  "devices": [
    "Pixel 7",
    "iPhone 15 Pro"
  ],
  "notify_on_fail": "slack",
  "enabled": true,
  "next_run_at": "2026-03-23T09:00:00Z",
  "created_at": "2026-03-22T10:00:00Z"
}
DELETE /api/mobile/schedules/:id Auth Required

Delete a mobile test schedule.

Path Parameters

id required
string — The UUID of the schedule to delete

Response

200 OK
{ "success": true }
Performance Testing
GET /api/performance/tests Auth Required

List all performance test configurations for the current team.

Response

200 OK
{ "tests": [{ "id": "uuid", "name": "Homepage Perf", "url": "https://example.com", "device": "desktop", "virtual_users": 10, "duration": 30, "perf_threshold": 50, "auto_create_bug": true, "created_at": "..." }] }
POST /api/performance/tests Auth Required

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. Pro/Team/Enterprise plans only.

Body Parameters

name required
string — Name for the performance test
url required
string — URL to test (web tests only)
device optional
string — Device to emulate: desktop (default), mobile, tablet
virtual_users optional
number — Number of virtual users for load testing (default: 10)
duration optional
number — Load test duration in seconds (default: 30)
perf_threshold optional
number — Lighthouse performance score threshold 0–100 (default: 50). Scores below this auto-create bug reports
auto_create_bug optional
boolean — Auto-create a bug report when thresholds are breached (default: true)
app_id optional
string — UUID of an uploaded mobile app (from POST /mobile/apps). Required for mobile performance tests
mobile_devices optional
array — List of BrowserStack device names to profile (e.g. ["Pixel 7", "iPhone 15 Pro"]). Each device creates one run and consumes 1 monthly credit
mobile_thresholds optional
object — Thresholds for mobile auto-bug creation: cpu_max (%), memory_peak (MB), fps_min, battery_drain_max (%), launch_time_max (ms). Exceeding any threshold triggers a bug report

Response

201 Created
{ "test": { "id": "uuid", "name": "Homepage Perf", "url": "https://example.com", ... } }
GET /api/performance/tests/:id Auth Required

Get a single performance test configuration by ID.

Path Parameters

id required
string — The UUID of the performance test
PATCH /api/performance/tests/:id Auth Required

Update a performance test configuration. Only include fields you want to change.

Path Parameters

id required
string — The UUID of the performance test

Body Parameters

name optional
string — Updated test name
url optional
string — Updated URL
device optional
string — Updated device
virtual_users optional
number — Updated virtual users count
perf_threshold optional
number — Updated score threshold
auto_create_bug optional
boolean — Updated auto-bug toggle
DELETE /api/performance/tests/:id Auth Required

Delete a performance test configuration and all its associated runs.

Path Parameters

id required
string — The UUID of the performance test to delete

Response

200 OK
{ "success": true }
POST /api/performance/run Auth Required

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.

Body Parameters

test_id required
string — The UUID of the performance test to run

Response

202 Accepted
{ "run": { "id": "uuid", "status": "running", "test_id": "uuid", "created_at": "..." } }
GET /api/performance/runs/:id Auth Required

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).

Path Parameters

id required
string — The UUID of the performance run
GET /api/performance/runs/:id/csv Auth Required

Download a CSV report for a performance run. Includes Lighthouse scores, Core Web Vitals, load test summary with latency percentiles, and test metadata.

Path Parameters

id required
string — The UUID of the performance run

Response

Returns text/csv with Content-Disposition: attachment; filename="performance-report-{id}.csv"

POST /api/performance/runs/:id/archive Auth Required

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.

Path Parameters

id required
string — The UUID of the performance run to archive

Response

200 OK
{ "success": true, "status": "archived" }
DELETE /api/performance/runs/:id/archive Auth Required

Unarchive a previously archived performance test run, restoring it to the default listing.

Path Parameters

id required
string — The UUID of the performance run to unarchive

Response

200 OK
{ "success": true, "status": "completed" }
GET /api/performance/usage Auth Required

Check monthly performance test usage against plan limits. Limits: Free=0, Pro=1/mo, Team=2/mo, Enterprise=10/mo, Admin=unlimited.

Response

200 OK
{ "used": 1, "limit": 2, "remaining": 1, "plan": "team", "period_start": "2026-03-01", "period_end": "2026-03-31" }
Security Scanning
POST /api/security/scans Auth Required

Create a security scan configuration. Requires Pro, Team, or Enterprise plan.

Request Body

name required
string — Scan name
scan_type required
string — web or mobile
target_url web only
string — URL to scan (domain must be verified via DNS TXT record)
app_id mobile only
string — UUID of the uploaded mobile app
scan_depth optional
string — quick (default, ~30s), regular (~5min), deep (up to 30min)
auth_config optional
object — { login_url, username, password } for authenticated web scanning
auto_create_bug optional
boolean — Auto-create bug reports for qualifying findings (default false)
min_severity_for_bug optional
string — Minimum severity for auto-bug: critical, high (default), medium, low
POST /api/security/run Auth Required

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.

Request Body

scan_id required
string — UUID of the scan configuration
scan_depth optional
string — Override the saved scan depth for this run

Response

201 Created
{ "id": "run-uuid", "status": "queued" }
GET /api/security/runs/{id} Auth Required

Get scan run status and results. Poll this endpoint while status is queued or running.

Response (completed)

200 OK
{
  "id": "run-uuid",
  "status": "completed",
  "security_score": 72,
  "total_findings": 15,
  "critical_count": 0,
  "high_count": 2,
  "medium_count": 5,
  "low_count": 4,
  "info_count": 4,
  "authenticated": true,
  "duration_seconds": 185
}
GET /api/security/domains Auth Required

List verified domains for the team. Use POST to add a domain and receive a DNS TXT verification token.

GET /api/security/schedules Auth Required

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.

Query Parameters

scan_id optional
string — UUID of the security scan config to filter by
POST /api/security/schedules Auth Required

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 your monthly security scan cap (Pro 2/mo, Team 5/mo, Enterprise 20/mo). Security+ and admin users bypass the cap.

Request Body

scan_id required
string — UUID of the security scan config to schedule
cron_expression required
string — 5-field cron expression (e.g. 0 9 * * 1-5 for weekdays at 9am)
timezone optional
string — IANA timezone (default: UTC)
notify_on_fail optional
string — Notify when the scan creates bug reports: none (default), email, slack, both
notify_email optional
string — Email address for failure notifications
slack_channel_id optional
string — Slack channel ID for failure notifications

Response

201 Created
{
  "id": "schedule-uuid",
  "scan_id": "scan-uuid",
  "cron_expression": "0 9 * * 1-5",
  "timezone": "America/New_York",
  "enabled": true,
  "next_run_at": "2026-04-06T13:00:00.000Z"
}
PATCH /api/security/schedules/:id Auth Required

Update a security scan schedule. Updating cron_expression, timezone, or re-enabling automatically recomputes next_run_at.

Request Body

cron_expression optional
string — Updated cron expression
timezone optional
string — Updated IANA timezone
enabled optional
boolean — Enable or disable the schedule
notify_on_fail optional
string — Updated notification setting
notify_email optional
string — Updated email
slack_channel_id optional
string — Updated Slack channel
DELETE /api/security/schedules/:id Auth Required

Delete a security scan schedule. Does not affect the parent scan config or any completed runs.

Code Review
GET /api/code-review/prs?repo=owner/repo Auth Required

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.

repostring (query)GitHub repo in owner/repo format
POST /api/code-review/review Auth Required

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. Team/Enterprise only.

repostringGitHub repo in owner/repo format
pr_numberintegerPull request number
GET /api/code-review/reviews Auth Required

List past code reviews for the team. Returns review ID, repo, PR number/title, quality score, severity counts, and timestamps. Ordered by most recent.

GET /api/code-review/reviews/:id Auth Required

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.

GET /api/code-review/settings Auth Required

Get code review settings for the team. Returns custom instructions, auto-review toggle, post-comments toggle, auto-bug creation config, and enabled status.

POST /api/code-review/settings Auth Required

Update code review settings. All fields optional — only provided fields are updated.

custom_instructionsstring?Custom review instructions sent to Claude
auto_reviewboolean?Auto-review PRs on open (Phase 2)
post_commentsboolean?Post inline comments to GitHub PR (Phase 3)
auto_create_bugboolean?Auto-create bug reports for critical findings
min_severity_for_bugstring?Minimum severity for auto-bug: critical, high, medium, low
GET /api/code-review/usage Auth Required

Check code review usage. Returns reviews used and plan info. Unlimited reviews on Pro, Team, and Enterprise plans.

GET /api/code-review/analytics Auth Required

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).

GET /api/github/webhooks Auth Required

List all GitHub webhooks registered for the team. Returns webhook ID, repo, active status, and creation date.

POST /api/github/webhooks Auth Required

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.

DELETE /api/github/webhooks Auth Required

Remove a GitHub webhook from a repository. Requires repo. Deletes the webhook from both GitHub and the bugAgent database.

POST /api/github/webhook Public (Webhook)

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.

GET /api/explorations Auth Required

List all Exploratory AI configs for the team. Returns name, target URL, status, agent count, selected strategies, last run info, and run count.

POST /api/explorations Auth Required

Create a new exploration config. Requires name and target_url. Optional: exploration_context (JSONB with focus_areas, instructions, auth_config, exclude_paths, max_depth, test_data), project_id, auto_create_bug, min_severity_for_bug, agent_count (1–10, clamped to plan limit; Pro: max 3, Team: max 10), agent_strategies (array of strategy IDs: happy_path, edge_case, security, accessibility, error_path, performance, mobile, data_integrity, navigation, custom).

POST /api/explorations/run Auth Required

Trigger an exploration run. Requires exploration_id. Dispatches to the runner for 5-phase execution (Recon, Plan, Execute, Analyze, Report). When agent_count > 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.

GET /api/explorations/runs/:id Auth Required

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.

GET /api/explorations/usage Auth Required

Check monthly Exploratory AI usage. Pro: 3/mo, Team: 50/mo, Enterprise: 50/mo.

GET/POST/PATCH/DELETE /api/explorations/schedules Auth Required

Manage exploration schedules. GET lists schedules. POST creates a new one (requires exploration_id and cron_expression). PATCH updates schedule (enable/disable, change cron). DELETE removes a schedule. Supports notifications via email or Slack on failure.

Compliance (Team/Enterprise)

Compliance Center

Automated SOC2, ISO 27001, and GDPR compliance evidence collection, config drift monitoring, access reviews, and security event tracking. Team and Enterprise plans only.

GET /api/compliance/connections

List connected compliance services for the team.

POST /api/compliance/connections

Connect or update a compliance service. Service must be one of: cloudflare, github, sentry, supabase, railway. Credentials are service-specific (e.g., api_token + zone_id for Cloudflare).

POST /api/compliance/collect

Trigger compliance evidence collection from all connected services. Returns run ID immediately; collection runs in background. Response includes id (run UUID), status ("running"), and usage (used/limit for current month).

GET /api/compliance/runs/:id

Get evidence collection run details including per-service evidence, findings, and metrics.

GET /api/compliance/drift

List config baselines and unresolved drift events.

POST /api/compliance/drift

Trigger a config drift check across all connected services. Compares current settings against baselines. Returns checks_performed, drift_detected count, and drift_events array with service, config_key, expected/actual values, and severity.

POST /api/compliance/drift/:id/resolve

Mark a config drift event as resolved.

POST /api/compliance/access-review

Generate a quarterly access review report. Optional body: quarter (e.g., "2026-Q2", defaults to current). Returns team members with roles/MFA/login status, API keys with usage/inactive days, summary stats, and actionable recommendations.

GET /api/compliance/events

Query the cross-service security event timeline.

Query params: source (cloudflare, sentry, github), severity (critical, high, medium, low, info), limit (max 100), offset

POST /api/compliance/backups

Trigger backup verification across connected services. Returns the verification rows just inserted into backup_verifications.

Supabase backup check: when the team's compliance_connections.credentials includes a management_token (a Supabase Personal Access Token from supabase.com/dashboard/account/tokens), the endpoint calls Supabase's Management API for the project's actual backup list and records whether the latest is younger than 24 hours. Without a management_token the row is recorded with status: "unknown" and a note explaining how to enable real verification.

Daily auto-run: a pg_cron job fires the verification at 04:00 UTC for every team that has the Management API token configured, so the evidence trail in backup_verifications stays continuous without anyone clicking the button.

GET /api/compliance/backups

List all backup verification rows for the team, newest first. Returns array of { id, team_id, service, backup_type, status, last_backup_at, metadata, verified_at }.

Recommendations

Suggestions & Recommendations

Use API keys over JWTs

API keys don't expire and are simpler for server-to-server or agent integrations. JWTs from /auth/login expire and require refresh logic.

Set a project on every report

Use the project field when creating reports to keep your bug data organized. If omitted, reports go to the team's default project.

Poll /usage before bulk operations

Check GET /usage before submitting many reports to avoid hitting plan limits mid-batch.

Use the MCP server for AI agents

If you're building an AI agent integration, the MCP server provides a more natural interface than raw REST calls. Both offer the same features.

Rotate API keys periodically

Use POST /keys/:id/regenerate to rotate keys without downtime — the new key keeps the same name and scopes.

Subscribe to the changelog

Stay up to date with API changes via the changelog page or RSS feed.