Welcome to the bugAgent API
The bugAgent public developer surface provides scoped, workspace-bound access to bug reports, test management, automation, and usage through REST and MCP.
Introduction
The bugAgent public API is organized around REST and MCP. REST requests with a JSON body use Content-Type: application/json; successful and failed REST responses are JSON unless an endpoint explicitly documents a file response. MCP uses JSON-RPC 2.0 over Streamable HTTP.
For runnable Node.js and Python clients, a direct MCP example, and a GitHub Actions failure-report workflow, start with the public bugAgent examples.
Secure
All requests over HTTPS. API keys are SHA-256 hashed at rest.
Fast
Bounded list operations, explicit pagination, and retry-safe workflows for integrations.
Agent-Friendly
Designed for both human and AI agent consumption. Consistent JSON schemas.
Supported developer surface
This page describes both public integration contracts and dashboard-session routes. The badge on each endpoint is the authority:
reports:read or another named scopeThe server enforces an exhaustive method/path policy. A workspace API key is accepted only on a contract carrying a named scope, must contain that exact scope, and receives 403 everywhere else. A new or unmatched implementation route is unavailable until it is classified; CI fails when the implementation, policy, and reference drift.
The public contract is additive within a version. Security fixes may tighten validation, authorization, rate limits, or resource bounds without advance notice. See Versioning and deprecation.
Tooling can consume the generated api-reference-index.json. It indexes every reference card and clearly distinguishes public, API-key, and dashboard-session audiences; it is intentionally not presented as a complete OpenAPI schema.
Base URL
All documented endpoint paths already include the /api prefix and are relative to this origin:
https://app.bugagent.com For example, to list bug reports:
GET https://app.bugagent.com/api/reports Authentication
The API uses three credential classes. They are not interchangeable:
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 https://app.bugagent.com/api/reports \
-H "Authorization: Bearer ba_live_your_key_here" Key lifecycle
- Create a separate key for each service or environment and grant only the required scopes.
- Store the key in a server-side secret manager or CI secret. Never ship it in browser JavaScript, a mobile binary, logs, screenshots, or source control.
- Record the key prefix and owner in your service inventory. The full secret is displayed only when created or regenerated.
- Rotate immediately after suspected exposure. Regeneration invalidates the previous secret; deletion revokes the key.
- Use a dashboard session, not a workspace API key, for account settings, billing, workspace membership, and administrator actions.
Endpoint badges define the supported authentication contract: a named scope such as reports:read allows a workspace API key with that scope; Auth Required means an interactive dashboard session unless the endpoint section explicitly documents API-key support. Do not rely on undocumented API-key access.
This rule is enforced centrally rather than left to individual handlers. Resource-level membership, role, project, ownership, and entitlement checks still run after the transport credential is accepted.
Core API and MCP key scopes
reports:readreports:writeusage:readjira:readcheck_jira_sync tool. Jira OAuth connection management remains a dashboard-session workflow.jira:writeautomations:writeautomations:runMobile API key scopes
mobile:readmobile:writemobile:runAgent test execution scopes
test_runs:readtest_runs:writeCreate a dedicated workspace-scoped key with only these two scopes for an execution worker. The selected suite supplies the project boundary; these routes do not accept a caller-provided project override.
Test case management scopes
test_cases:readtest_runs:read for execution-worker compatibility.test_cases:writeusage:readGET /api/usage and the MCP get_usage tool.Test-case and usage scopes are opt-in and granted per key. A key that was not granted them keeps the closed surface — the same least-privilege boundary external agents rely on.
Browser Capture Key
The bugAgent browser SDK uses a project-bound public key beginning with ba_pub_. Send it only in X-BugAgent-Key. The server verifies the key's sessions:capture scope and requires the request Origin to exactly match an allowed origin configured by a workspace owner, administrator, or manager.
- A browser key is intentionally visible to browser code, but it is restricted to one workspace, one project, configured origins, capture quotas, and capture endpoints.
- A secret
ba_live_workspace key is rejected by browser-capture endpoints and must never be embedded in frontend code. - Browser keys cannot read captured sessions, bug reports, team data, or settings. Review remains an authenticated dashboard action.
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.
Tenancy and identifiers
Every API key belongs to one workspace. The server derives that workspace from the credential; callers cannot use a project or report identifier to cross the workspace boundary. A resource outside the authorized workspace is returned as not found rather than revealing whether it exists.
workspace_id / team_idteam_id; both refer to the same tenant boundary.project_idGET /api/projects or MCP list_projects.short_idWORKSPACE-PROJECT-NNN form, for example TEST-BA-123. Some older reports also expose a legacy workspace-only ID.idProject names and slugs are not globally unique. Resolve them inside the authenticated workspace and reject ambiguity in your integration rather than selecting the first match. Never cache one workspace's project IDs for use with another workspace's key.
Pagination, retries, and idempotency
List responses use endpoint-specific pagination fields. Most use limit with offset or page with per_page; follow the fields documented on that endpoint and stop when has_more is false or the returned item count is below the requested page size.
- Use conservative page sizes and preserve the same filters while paging.
- Retry
429,502,503, and504with exponential backoff and jitter. HonorRetry-Afterwhen present. - Do not automatically retry other
4xxresponses. Correct the request or credential first. - Retry reads freely. Retry writes only when the operation documents idempotency or your client can confirm the first attempt did not commit.
- The external-agent execution API uses caller-supplied
external_run_idfor safe start/resume behavior. Reuse it after a timeout.
Rate limits and resource controls
Limits vary by endpoint, plan, credential, and abuse signal. A request that exceeds a request-rate or product-usage limit returns 429 or an endpoint-specific quota error. Integrations must bound concurrency and avoid polling faster than the endpoint guidance.
delay = min(30_000, 500 * 2 ** attempt) + random(0, 250) Use webhooks or scheduled jobs where offered, cache stable metadata such as project IDs, and avoid treating deletion as a way to reset monthly usage. Contact support before sustained high-volume imports or automation dispatch.
Versioning and deprecation
Versioned public contracts include /api/v1/ in the path. Existing unversioned endpoints with named scope badges are also supported, but new integration-grade workflows will prefer a versioned path.
- Additive response fields may appear at any time. Ignore fields your client does not understand.
- Breaking request or response changes require a new version or a documented migration period.
- Deprecated endpoints return an explicit status or deprecation guidance before removal when security permits.
- Security and tenant-isolation fixes may reject requests that were previously accepted incorrectly.
Pin and test your integration behavior, not a copied response snapshot. The public examples run in CI and demonstrate the maintained contract patterns.
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 129 tools (bug reports, projects, automations, explorations, security scans, etc.) to any MCP-compatible client — Claude Desktop, Claude Code, OpenAI Codex CLI, 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.
Eight connection options (macOS + Windows)
1. MCP Inspector (web UI, recommended for first-time testing)
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):
{
"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)
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. OpenAI Codex CLI
Add the hosted server to ~/.codex/config.toml, then export the API key before starting or restarting Codex:
[mcp_servers.bugagent]
url = "https://mcp.bugagent.com/mcp"
bearer_token_env_var = "BUGAGENT_API_KEY" export BUGAGENT_API_KEY="ba_live_YOUR_KEY_HERE" 5. 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.
6. VS Code with Continue extension (Mac + Windows)
Install the Continue extension, then edit ~/.continue/config.json (Mac) or %USERPROFILE%\.continue\config.json (Windows):
{
"mcpServers": [
{
"name": "bugagent",
"type": "streamable-http",
"url": "https://mcp.bugagent.com/mcp",
"requestOptions": {
"headers": {
"Authorization": "Bearer ba_live_YOUR_KEY_HERE"
}
}
}
]
} 7. 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.
- In bugAgent: Settings → Developers → MCP Connectors → Generate 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 theclient_idandclient_secretshown once on the success screen. - 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
- Server URL:
- 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.
8. curl / Terminal (JSON-RPC 2.0)
MCP Streamable HTTP speaks JSON-RPC 2.0. The Accept: application/json, text/event-stream header is required.
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}}
}' $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 Error Handling
The API uses standard HTTP status codes and returns errors as JSON:
{
"error": "Description of what went wrong"
} | Code | Meaning |
|---|---|
200 | Success |
201 | Created |
400 | Bad request — invalid or missing parameters |
401 | Unauthorized — missing or invalid authentication |
403 | Forbidden — insufficient permissions |
404 | Not found — resource doesn't exist or you don't have access |
409 | Conflict — resource already exists |
410 | Gone — an upload or temporary resource is no longer available |
411 | Length required — a bounded upload requires Content-Length |
413 | Payload too large |
422 | Unprocessable — validation failed |
429 | Rate or usage limit exceeded — honor Retry-After when present |
500 | Internal server error |
502, 503, 504 | Transient upstream or service failure — retry a bounded number of times with backoff; a security control may fail closed with 503 |
Rate-limited endpoints may also return RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset. Clients must tolerate additive error fields and must not expose response bodies containing identifiers or diagnostics to unrelated tenants.
Public examples and SDK status
Use the maintained, MIT-licensed public examples today. Official language SDKs remain planned and are not yet published.
Node.js REST quickstart
Available now, zero dependencies
GitHub Actions
Available now, copy-ready workflow
MCP quickstart
Available now for compatible agents
Official language SDKs
Planned, not yet published
Any standards-compliant HTTP client can call the REST API directly. The public Node example is a small client, not an official versioned SDK.
Contribute
We welcome feedback on our SDKs and API examples. If you have suggestions, improvements, or another language you would like us to support, email feedback@bugagent.com.
Prefer a form? Contact us and include the SDK, endpoint, or language you want us to review.
License
The examples in the public TestLauncher repository are available under the MIT License. Future SDKs will publish their own version and license metadata when released.
/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.
Request Body
email requiredpassword requiredfull_name optionalResponse
{
"userId": "uuid",
"email": "agent@example.com"
} /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.
Request Body
email requiredpassword requiredResponse
{
"userId": "uuid",
"email": "agent@example.com",
"accessToken": "eyJhbG...",
"refreshToken": "refresh_...",
"expiresAt": 1711234567
} /api/reports reports:read List bug reports for the authenticated user's account and team. Project and workspace filters are applied before pagination. Each report includes project_id, project, short_id, legacy_short_id, and project_short_id so API clients can link the report without guessing project context.
Query Parameters
workspace, workspace_id, team_id optionalworkspace_id or team_id for a workspace UUID, or workspace for a UUID, exact workspace name, or workspace ticket prefix. API-key calls remain restricted to the key-scoped workspace.project, project_id, project_slug, project_prefix optionalproject_id accepts the project UUID. project_slug accepts the project slug. project_prefix accepts the project ticket prefix from project-scoped IDs such as TEST-BA-001. Generic project accepts UUID, slug, exact name, or ticket prefix.type optionalui, performance, crash, security, logic, data, network, accessibility, compatibility, functional, ui-ux, data-integrity, feature-request, enhancement, technical-debt, documentation, devops, ux-improvement, integrationseverity optionals1 (Blocker), s2 (Critical), s3 (Major), s4 (Minor). Legacy values still accepted for backward compatibility: critical, high, medium, low.status optionalnew, awaiting-triage, confirmed, in-progress, blocked, resolved, retesting, closed, reopened. Note the hyphens in awaiting-triage and in-progress — underscored variants will not match anything on the kanban.is_epic optionaltrue) or standard reports/stories (false).queue optionalagent applies the canonical Agent Queue pickup filter: status in new/awaiting-triage/confirmed, severity s1–s3, ordered by severity then oldest first. Composes with the other filters (e.g. reported_by).resolution optionalfixed, duplicate, works-as-designed, cannot-reproduce, will-not-fix, need-more-info, unresolved.root_cause optionalregression, missing-requirement, documentation, incomplete-refactor, not-a-bug, requirements-mismatch.search optionalTEST-24 and project IDs such as TEST-BA-24 resolve to their exact report. A bare positive integer such as 24 is treated as an exact identifier lookup across both legacy ticket_number and current project_ticket_number values rather than a broad text search.limit optionaloffset optionalExample
curl "https://app.bugagent.com/api/reports?project=bugagent&type=crash&limit=10" \
-H "Authorization: Bearer ba_live_..." /api/reports reports:write 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. The response returns both the workspace-scoped legacy ID and the project-scoped short ID, along with project_id and project metadata.
Request Body
title requireddescription optionaltype optionalui, 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 optionals1 (Blocker), s2 (Critical), s3 (Major, default), s4 (Minor). Legacy values still accepted: critical, high, medium, low. Auto-classifiers (heuristic + Claude) now emit s1–s4.priority optionalurgent, high, normal, low. Severity reflects technical impact (s1–s4); priority reflects how urgently the team should act. Omit to leave unset.project optional404 and never falls back.project_id, project_slug, project_prefix optionalproject_id for UUID, project_slug for slug, or project_prefix for the project ticket prefix used in short IDs like TEST-BA-001.is_epic optionaltrue to create an Epic container. Epics cannot have parent Epics.parent_epic_id optionalnull — Parent Epic UUID or short ID. The parent must be an Epic in the same workspace and project, and the caller must have explicit project access.environment optional{ browser, os, device, url }metadata optionalformat_description optionaltrue, the description is reformatted into a structured bug template using AI before saving. Defaults to false.time_spent_seconds optional0.Response
{
"id": "1fb72a2c-87c7-4adf-90e7-9bd81a8f34b7",
"ticket_number": 545,
"project_ticket_number": 42,
"short_id": "WRKID-APP-042",
"legacy_short_id": "WRKID-545",
"project_short_id": "WRKID-APP-042",
"project_id": "7b12c88b-4af5-4a21-9ef4-e74d98cb1337",
"team_ticket_prefix": "WRKID",
"project_ticket_prefix": "APP",
"project": {
"id": "7b12c88b-4af5-4a21-9ef4-e74d98cb1337",
"name": "App",
"slug": "app",
"ticket_prefix": "APP"
},
"title": "Login button unresponsive on iOS",
"is_epic": false,
"parent_epic_id": null,
"parent_epic": null,
"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",
...
} /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 requiredtitle optionalResponse
{ "formatted": "## Summary\nLogin button is unresponsive...\n\n## Steps to Reproduce\n1. Navigate to...\n\n## Expected Behavior\n...\n\n## Actual Behavior\n..." } /api/reports/:id reports:read 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.
Hierarchy fields are returned in snake case: is_epic, parent_epic_id, parent_epic, and epic_progress. Epic detail responses include only the first bounded epic_children page plus epic_children_page metadata. Use the children endpoint below for subsequent pages.
Response Fields (includes all standard fields plus)
quality_scorequality_breakdownreproduction_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_statusclaude_status cycles analyzing → done / failed.claude_draft_analysis, claude_challenger_critique, claude_challenger_modelclaude_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_models1/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_atdev_notes_stale/api/reports/:id/children Auth Required List one page of child reports for an Epic. The Epic must belong to a project the caller can access. Aggregate progress is calculated in PostgreSQL and does not require loading every child report.
Query Parameters
limit optionaloffset optionalThe response contains children, total, limit, offset, has_more, and epic_progress. Progress counts resolved and closed children as complete.
/api/reports/:id reports:write 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, priority, internal_notespriority accepts urgent / high / normal / low (fix urgency, independent of severity) or null to clear it. internal_notes are private and not synced to Jira.status optionalnew, awaiting-triage, confirmed, in-progress, blocked, resolved, retesting, closed, reopened. The hyphens in awaiting-triage and in-progress are deliberate — underscored variants won't show up on the kanban.resolution optionalnew 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, unresolved (a fix was attempted but the issue isn't fully resolved).root_cause optionalresolution 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 optionalnull 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 optionalis_epic optionalparent_epic_id optionalnull — Reparent by UUID/short ID, or pass null to detach. Reparent/detach is atomic. Parent and child must be in the same authorized workspace and project./api/reports/:id reports:write Permanently delete one report and its stored attachments. The identifier may be the canonical UUID or a short ID accepted by the report resolver. The caller must be able to access the report's workspace and project. Treat 404 as missing or unauthorized.
/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 requiredWRKID-545) — The bug report to assign.assigned_to requiredResponse
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).
/api/reports/links Auth Required Create a directional semantic link between two bug reports in the same workspace — the user-curated counterpart to the auto-detected similar_reports field. Both reports must belong to the same team; cross-team links are rejected (the database trigger enforces this as well as the API check). The inverse perspectives (duplicated-by / subtask-of / blocks / blocks-testing) are not stored separately — they're derived at read time from the same row.
Request Body
report_id requiredWRKID-545) — The "from" side of the link.target_report_id requiredlink_type requiredduplicate-of, parent-of, related-to, depends-on, testing-blocked-by. related-to is symmetric; the other four are directional.Response
Returns a JSON body with a link object containing id, from_report_id, to_report_id, link_type, created_at, and created_by. 409 Conflict if a link of this type between these two reports already exists.
/api/reports/links Auth Required Remove a previously-created bug-report link by its UUID. The caller must be an active member of the workspace that owns the link.
Request Body
link_id requiredlist_bug_report_links tool).Response
Returns success: true on a clean delete. 404 if the link does not exist (or the caller cannot see it).
/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 requiredfiles requiredimage/* (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./api/reports/upload-preflight Auth Required Upload a single image before a report ID exists, so it can be embedded inline in the bug description as a markdown image reference. Used by the "Report a Bug" composer when pasting a screenshot — the returned URL is inserted at the cursor position as . Files land at a preflight storage path and are not linked to any bug report row; orphaned files are cleaned up by a periodic storage sweep. Max 25 MB.
Form Fields
file requiredimage/* MIME type). Max 25 MB.Response
{ "url": "https://...supabase.co/storage/v1/object/public/bug-attachments/.../preflight/...", "filename": "screenshot-1234567890.png" } /api/reports/flush Auth Required Bulk delete old bug reports. Requires owner or admin team role.
Request Body
months requiredproject_id optionalResponse
{ "deleted": 42 } /api/reports/comments Auth Required Add a comment to a bug report, or toggle an emoji reaction on a comment.
Create Comment
report_id requiredcontent requiredToggle Reaction
action required"react"comment_id requiredemoji required/api/reports/comments Auth Required Edit a comment. Only the author can edit their own comments.
comment_id requiredcontent required/api/reports/comments Auth Required Delete a comment. Only the author can delete their own comments.
comment_id required/api/projects reports:read List all accessible projects in the key-bound or active workspace. Each project includes id, team_id (the workspace UUID), name, slug, ticket_prefix, description, is_default, and created_at. Use the exact workspace and project UUIDs with GET /api/reports, POST /api/reports, and MCP project filters.
/api/projects Auth Required Create a new project. The first project is automatically set as default. Subject to plan limits.
Request Body
name requireddescription optionalis_default optional/api/projects Auth Required Update a project's name, description, or default status.
id requiredname, description, is_default/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 requiredWhat Gets Deleted
- 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
Errors
403400404/api/keys Auth Required List active, non-revoked workspace API keys. Requires an authenticated dashboard session and an owner, administrator, or manager role. API keys cannot manage other keys. Only the key prefix is returned — full keys are shown once at creation.
/api/keys Auth Required Generate a new API key. Requires an authenticated dashboard session and an owner, administrator, or manager role. API keys cannot create other keys. The full key (starting with ba_live_) is returned only once — store it securely. Free workspaces may keep at most two active workspace API keys.
Request Body
name requiredscopes optional["reports:read", "reports:write"]Response
{
"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"
} /api/keys/:id Auth Required Revoke an API key. Requires an authenticated dashboard session and an owner, administrator, or manager role. The key is soft-deleted and can no longer be used for authentication.
/api/keys/:id/regenerate Auth Required Revoke the current key and generate a new one with the same name and scopes. Requires an authenticated dashboard session and an owner, administrator, or manager role. Returns the new full key and immediately invalidates the previous secret.
/api/profile Auth Required Get the authenticated user's profile.
Response
{
"id": "uuid",
"email": "user@example.com",
"full_name": "Jane Smith",
"avatar_url": null,
"plan": "enterprise",
"is_agent": false,
"created_at": "2026-03-10T08:00:00Z"
} /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/api/profile/password Auth Required Change your password.
password required/api/settings Auth Required Get your profile info and notification preferences.
Response
{
"fullName": "Jane Smith",
"email": "user@example.com",
"isAgent": false,
"plan": "enterprise",
"notifications": {
"emailUsageWarning": true,
"emailBugReportAssignment": true,
"emailTestCaseAssignment": true
}
} /api/settings Auth Required Update notification preferences.
email_usage_warning optional/api/usage usage:read Get your current plan usage (reports used, limit, remaining, reset date).
Response
{
"used": 47,
"limit": 5000,
"remaining": 4953,
"plan": "enterprise",
"period": "monthly",
"resetsAt": "2026-04-01T00:00:00Z"
} /api/stats Auth Required Get report statistics with daily counts, breakdowns by type, severity, and status.
Query Parameters
days optionalResponse
{
"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 }
} /api/checkout Auth Required Deprecated compatibility endpoint. Enterprise enrollment is sales-assisted, so this endpoint never creates a checkout session or changes a workspace plan.
Response
{
"error": "Enterprise enrollment is sales-assisted. Contact Sales to change plans.",
"action": "contact_sales",
"url": "https://bugagent.com/#pricing"
} /api/billing-portal Auth Required Get a URL to the Stripe billing portal where users can manage subscriptions, payment methods, and invoices.
Response
{ "url": "https://billing.stripe.com/..." } /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"invite"email requiredrole optional"contributor" (default), "manager", or "owner". Managers can only invite contributors and managers./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"edit_name"member_id requirednew_name required/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"change_role"member_id requirednew_role required"contributor", "manager", or "admin"/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"remove_member"member_id required/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/api/create-team Auth Required Create a new workspace. A user may own up to five workspaces; invited memberships do not count toward this limit. Every additional workspace starts on Free, regardless of the creator’s other workspace plans. Use Feedback or Talk to Sales to request Enterprise enrollment or additional workspaces. If no name is provided, an uplifting name is auto-generated (e.g. "Radiant Foxes"). A Default Project is automatically created so the workspace always has at least one project.
Request Body (JSON)
name optionalResponse
{
"success": true,
"team": {
"id": "uuid",
"name": "Radiant Foxes",
"plan": "free"
}
} Errors
403400Skills 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
| Skill | Connection | Description |
|---|---|---|
| GitHub | OAuth | Sync repositories for Playwright automation scripts. See GitHub endpoints. |
| Jira | OAuth 2.0 | Bi-directional bug sync with comments, attachments, and severity. See Jira endpoints. |
| Claude | API Key | Root cause analysis for bug reports. See Claude endpoints. |
| Slack | Webhook | Notifications 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 support@bugagent.com.
awaiting-triage) can be routed with an explicit push entry in the per-project field mapping (for example, into To Do); the route is one-way — the Jira column keeps its own pull mapping.Setting up a workspace, end to end
The pieces below fit together in one order. Every step after the connect is manager-and-above; ordinary members can sync individual reports but never see or change the wiring.
- Connect —
GET /api/jira/connectstarts OAuth. The connection is team-scoped and shared; it survives the connecting user leaving. - Map projects —
POST /api/jira/mappingsper bugAgent project. A mapping decides where that project's reports land, so no member can see or push into another team's Jira projects. - Pick a sync mode —
manual(default),auto_new, orauto_all, set workspace-wide viaPOST /api/jira/settingsand overridable per project viasync_modeon the mapping. Resolution is mapping override → workspace default → manual: a project pinned tomanualstays out even when the workspace isauto_all. - Map the values —
GET /api/jira/project-metareturns the project's real statuses and priorities;POST /api/jira/suggest-field-mapproposes a mapping from those names alone; save it asfield_mapon the mapping. Nothing is applied without a save. - Merge what already exists —
POST /api/jira/bulk-syncwithpreview, thenstart. Runs are server-driven: safe to close the page.
POST /api/jira/bulk-sync { "action": "preview" }
→ { "perProject": [{ "jira_project_key": "BA", "jira_issues": 128,
"linked_reports": 12, "unlinked_reports": 759 }] }
POST /api/jira/bulk-sync { "action": "start" }
→ { "jobId": "8b43ce5f-…" }
POST /api/jira/bulk-sync { "action": "status" }
→ { "job": { "status": "running",
"progress": { "imported": 0, "updated": 2, "pushed": 270, "errors": 0 },
"finished_at": null } } field_map. Unlinked bugAgent reports are pushed to Jira. Nothing is deleted on either side, and selecting auto_all never starts a merge by itself — only an explicit start does./api/jira/connect Manager+ Start the Jira OAuth 2.0 flow on either bugAgent plan. Redirects to Atlassian's authorization page. The resulting connection is stored against the initiating active workspace, not the individual user, and the callback fails if the active workspace changes during authorization.
/api/jira/projects Manager+ Fetch all Jira projects accessible by the team's shared connection. Restricted to workspace owners, admins, and managers — this browses the connected Jira site, which ordinary members should not see. Returns the default project key if one is saved.
Response
{
"projects": [
{ "id": "10001", "key": "BUG", "name": "Bug Tracker", "avatar": "https://..." },
{ "id": "10002", "key": "PROJ", "name": "Main Project", "avatar": "https://..." }
],
"defaultProjectKey": "BUG"
} /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 requiredprojectKey optionalResponse
{
"success": true,
"jiraKey": "PROJ-1234",
"jiraUrl": "https://your-site.atlassian.net/browse/PROJ-1234"
} /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 requiredSync Behavior
report.updated_at vs Jira fields.updated — whichever platform was modified more recently determines the value. The other side is updated automatically.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.jira_attachment_id to prevent duplicates across sync cycles.Response
{
"success": true,
"results": [
"Description synced",
"Priority pushed to Jira (high — last updated)",
"2 comment(s) pushed to Jira",
"1 attachment(s) pulled from Jira"
]
} /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 requiredResponse
{
"hasChanges": true,
"jiraUpdatedAt": "2026-03-18T15:30:00.000Z",
"changes": {
"title": { "jira": "Updated title in Jira", "bugagent": "Original title" },
"severity": { "jira": "high", "bugagent": "medium" }
}
} /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 requiredmerged requiredtitle, severity, status, type. Each value is the chosen winner (from bugAgent or Jira)./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"update_settings" or "disconnect"auto_push optionaldefault_project_key optionalsync_mode optional"manual" (default), "auto_new" (reports created by team members push to Jira automatically through the project mapping), or "auto_all" (as auto_new, plus eligibility for the explicit bulk sync — selecting the mode never starts a merge by itself)unlink_reports optional"disconnect": also clear the Jira links from every synced report. By default disconnect stops the sync but keeps existing links clickable; project mappings are always removed with the connection./api/jira/mappings Auth Required List the workspace's project mappings — which Jira project each bugAgent project syncs to. Sync resolution order: explicit key on the push, then the report's project mapping, then the default project key.
Response
{
"mappings": [
{ "project_id": "…", "jira_project_key": "ENCDEV", "jira_project_name": "enCappture Dev" }
]
} /api/jira/mappings Manager+ Create, update, or remove a project mapping. Sending an empty jira_project_key removes the mapping and the project falls back to the default. Mappings are removed automatically when the Jira connection is disconnected or reconnected to a different site.
Request Body
project_id requiredjira_project_key optionaljira_project_name optionalsync_mode optional"manual", "auto_new", "auto_all", or null to inherit the workspace default. The override wins wherever mode is consulted — auto-push on creation and bulk-sync eligibility both resolve per project.field_map optionalstatus— Jira status name (lowercase) → bugAgent status. Pull direction.status_push— bugAgent status → Jira status name. Push direction, one-way. Required for statuses that exist only in bugAgent (awaiting-triage,retesting) — nothing in Jira maps back to them, so without an entry they never push.severity— Jira priority name (lowercase) →s1–s4. Pull direction.severity_push—s1–s4→ Jira priority name. Push direction.
{
"project_id": "8f2c…",
"jira_project_key": "BA",
"sync_mode": "auto_all",
"field_map": {
"status": { "to do": "new", "in progress": "in-progress", "qa review": "retesting", "done": "resolved" },
"status_push": { "awaiting-triage": "To Do", "confirmed": "To Do", "closed": "Done" },
"severity": { "p0 - showstopper": "s1", "p1": "s2", "p2": "s3" },
"severity_push": { "s1": "P0 - Showstopper", "s2": "P1", "s3": "P2", "s4": "P2" }
}
} /api/jira/project-meta Manager+ The live value-spaces of one Jira project — its workflow statuses and the site's priorities. Feeds the field-mapping modal. Query: projectKey.
/api/jira/bulk-sync Manager+ The explicit "Sync everything now" action. Requires at least one mapped project whose effective mode is auto_all (per-project override, else the workspace default); only those projects join the merge. It never runs on its own. Actions: preview (dry-run counts, nothing written), start (creates the job and kicks a server-side run — one running job per workspace, and it is safe to close the page: the server drives it to completion, and a watchdog revives runs interrupted by a deploy), status (live progress — the dashboard polls this to show a run in flight, and stamps when the last full sync finished), cancel, tick (processes one batch under the same lease; kept for compatibility, no longer required). Jira issues already linked to a report are updated, never duplicated; unlinked issues become reports translated through the project's field map; unlinked reports push to Jira. Nothing is deleted on either side.
/api/jira/suggest-field-map Manager+ AI-assisted field mapping: send the Jira project's status and priority names (never ticket content) and receive a proposed mapping, validated server-side against both value sets. The proposal prefills the modal for review — it is never applied automatically.
Request Body
statuses requiredpriorities optional/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 requiredResponse
{
"updates": [
{
"id": "uuid",
"old_status": "new",
"new_status": "in-progress",
"jira_status": "In Progress"
}
],
"synced": 1
} /api/automations Auth Required Deprecated compatibility endpoint. Browser recording now runs in the authenticated Automate Web workspace. Use POST /api/automations/create to create an automation from a Playwright script.
Response
{ "error": "Browser automation recording has moved to the authenticated Automate Web workspace." } /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 requiredtarget_url optionalpage.goto(...) call in the script.script optionalstatus optional"active" or "draft". Default: "draft"project_id optionalAuthentication
Requires session authentication (dashboard) or an API key with automations:write scope.
Plan
Available on both plans within workspace limits. Live-browser execution requires Enterprise.
Response
{
"id": "uuid"
} /api/automations Auth Required List all automations for the user's active team. Supports pagination and filtering by status.
Query Parameters
status optionalactive, paused, archivedproject_id optionalcreated_by optionalpage optionallimit optionalResponse
{
"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
} /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. Use each entry's durable version value as version_label: "vN" when replaying it.
Response
{
"id": "uuid",
"name": "Login flow smoke test",
"steps": [
{ "action": "navigate", "url": "https://app.example.com/login" },
{ "action": "fill", "selector": "#email", "value": "test@example.com" },
{ "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", "version": 12 },
{ "script": "// the state before an AI-optimize ran", "source": "before_optimize", "timestamp": "2026-03-21T07:50:00Z", "version": 13 }
],
"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": "v14", "script_version_source": "current" }
]
} Notes
scriptis always the current live version — the one Run Now executes when both version selectors are omitted.script_versionsis a chronological stack (oldest first) of up to 100 prior states ofscript. An entry is pushed wheneverscriptchanges via PATCH, optimize, or the BrowserStack rewrite. Each entry records{ script, source, timestamp, version };versionremains stable when older entries rotate out. Preferversion_label: "vN"onPOST /automations/runsorPOST /v1/automations/run. Legacyversion_indexremains supported, but must not be sent withversion_label.- Each entry in
recent_runscarries the durable version it executed asscript_version_label("vN") and whether it was live or historical inscript_version_source. Legacy runs may still report"current". The per-run script text itself is on the run detail (script_snapshot) — omitted from list responses to keep them small.
/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 optionalsteps optionalscript optionalscript, the previous text is pushed onto script_versions (capped at 100 entries, oldest-evicted) so callers can replay or undo it.version_source optionalscript_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 optionalnull to remove schedulestatus optionalactive, paused, or archivedauth_enabled optionalauth_username is optional (leave blank for password-only gates).auth_signin_url optionalpage.goto().auth_username optionalauth_password optionalAUTOMATIONS_AUTH_KEY. Pass an empty string to clear the stored password; omit to leave it unchanged. Never returned in responses.login_profile_id optionalnull — Set or clear the automation’s default project-scoped login profile. Reusable authenticated sessions require a profile.auth_session_mode optionalfresh-login (default) or reuse — Existing automations remain on fresh login. Reuse is intended for scripts that begin after authentication and requires both login_profile_id and target_url.Response
{
"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|falseso 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 fornetworkidle. 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. - With
auth_session_mode=reuse, bugAgent stores Playwright cookies, local storage, and supported IndexedDB state as encrypted server-side ciphertext. State is isolated by workspace, project, login profile, normalized target origin, provider, browser, and OS family. BrowserStack receives the run-scoped state through its PlaywrightstorageStateconfiguration; the runner deletes the temporary file after execution. - Session state is never returned by dashboard, REST, or MCP reads. Profile rotation, visibility changes, deletion, expiry, or a manual Reset session invalidates it. Concurrent refreshes use a bounded lease, and stale parallel callbacks cannot overwrite newer state.
- Playwright
sessionStorageis not reusable. Native APK/IPA automation remains separate. MFA, CAPTCHA, and interactive SSO may require a fresh user-assisted profile/session rather than automatic refresh.
/api/automations/:id Auth Required Permanently delete an automation and all its associated run history. This action cannot be undone.
Response
{ "deleted": true } /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 requiredautomation_id optionalstart_url optionalResponse
{
"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', 'test@example.com');\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"
} /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 requiredResponse
{
"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."
} /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 its durable version_label to POST /automations/runs instead.
URL Parameters
id requiredResponse
{
"script": "// previous version of the script...",
"version": 2,
"versions_remaining": 1
} { "error": "No previous versions available to undo" } /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 requiredResponse
{
"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_KEYon the Dashboard service. Returns500with 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/:idincluding"script": <rewritten>and (optionally)"version_source": "bs_compat_rewrite"so the entry shows up in version history with that tag.
/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 requiredversion_label optional, preferred"v103". Resolve it from the entry's version field returned by GET /automations/:id. The server resolves the label against the authorized automation at dispatch time, so history rotation cannot change the selected script. A pruned label returns 409. Do not send version_index at the same time.version_index optionalversion_label, because array indices shift when the 100-entry history rotates. Non-integer, negative, or out-of-range values return 400. When both selectors are omitted, the current live script runs.environment optionaldevice optional"desktop"browserstack optionaltrue 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 storage — video_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"chrome", "firefox", "safari", "edge"bs_os optional"Windows", "OS X", "android" for a real Android device, or "ios" for a real iPhone.bs_os_version optional"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.Response
{
"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.
/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 optionalstatus optionalqueued, running, passed, failed, cancelledpage optionallimit optionalResponse
{
"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
} /api/v1/automations/run automations:run 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 requiredversion_label optional, preferred"v103". The label is resolved at dispatch time and a pruned label returns 409. Do not send version_index at the same time.version_index optionalversion_label. When both selectors are omitted, the current live script runs. The run record captures the resolved script snapshot and failed-run reports deep-link with the durable label.login_profile_id optionalnull — Run the same script with a specific authorized login profile. Omit this field to use the automation's default profile; pass null to disable the default for this run. The API key owner must be allowed to use the profile in the automation's project.auth_session_mode optionalfresh-login or reuse — Override the automation’s saved authentication behavior for this run. Reuse requires an authorized same-project login profile and target URL; missing or expired state is refreshed once before the script runs.environment optionalcallback_url optionaldevice optional"desktop". See POST /automations/runs for the full list of supported device values.Headers
Authorization requiredBearer ba_live_... — API key with automations:run scopeResponse
{
"run_id": "uuid",
"automation_id": "uuid",
"status": "queued",
"device": "desktop",
"poll_url": "/api/v1/automations/runs/uuid",
"queued_at": "2026-03-21T14:30:00Z"
} /api/v1/automations/runs/:id automations:run 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 requiredBearer ba_live_... — API key with automations:run scopeResponse
{
"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" }
]
} tests/bugagent/{name}.spec.ts and stay in sync on every edit./api/github/connect Auth Required Initiate the GitHub OAuth flow. The endpoint stores a short-lived CSRF state cookie and redirects the browser to GitHub. Once the user authorizes, GitHub returns to the fixed bugAgent callback. Connect from Settings → Integrations in the dashboard.
Response
Location: https://github.com/login/oauth/authorize?client_id=... /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
{
"repos": [
{
"id": 123456,
"full_name": "acme/web-app",
"default_branch": "main",
"private": true
}
]
} /api/github/branches Auth Required List the branches of a repository via the connected GitHub account. Used to populate the branch dropdown when configuring OKF/OQA knowledge sync for a project.
Query Parameters
owner/repo. Response
{ "branches": ["main", "develop", "oqa-knowledge"] } /api/github/map-repo 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 requiredgithub_repo requiredowner/repo format (e.g. acme/web-app)Response
{
"success": true,
"github_repo": "acme/web-app"
} /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
{
"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_statusissha_conflict, the remote file was modified outside bugAgent. Resolve the conflict in GitHub, then retry the script save or push. To change the project’s repository association, usePOST /api/github/map-repo. - Scripts are pushed to
tests/bugagent/{automation-name-slug}.spec.tsusing the GitHub Contents API.
/api/github/settings Manager+ Disconnect the GitHub integration. Removes the OAuth token and all project-to-repo mappings. Scripts already pushed to GitHub are not deleted.
Request Body
{ "action": "disconnect" } Response
{
"success": true
} /api/notes Auth Required List notes for the authenticated user's active team and project. Returns notes the user owns, project-team notes, and restricted notes that include the user. Supports full-text search, project filtering, author filtering, folder filtering, wiki filtering, tag filtering, archive filtering, sorting, and date range filtering.
Query Parameters
search optionalproject optionalauthor optionalwiki optionaltrue/1 for wiki pages only, false/0 for non-wiki notes onlytag optionalfolder_id optionalunfiled for notes without a folder. Child folders are included by defaultarchived optionalactive (default), archived, or allsort optionalnewest (default), oldest, updated, or title. Pinned notes are always returned firstfrom optionalto optionalpage optionalpage_size / per_page optionalResponse
{
"notes": [
{
"id": "uuid",
"title": "Checkout flow observations",
"format": "markdown",
"visibility": "shared",
"project_id": "uuid",
"folder_id": "uuid",
"author_id": "uuid",
"author_name": "Jane Smith",
"last_editor_name": "Jane Smith",
"time_spent_seconds": 1820,
"wiki_page": true,
"pinned": false,
"archived_at": null,
"tags": ["regression", "checkout"],
"attachments_count": 2,
"content_preview": "Observed checkout behavior...",
"created_at": "2026-03-22T10:00:00Z",
"updated_at": "2026-03-22T10:30:00Z"
}
],
"total": 42,
"page": 1,
"per_page": 20
} /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 optionalcontent optionalformat optionalmarkdown (default), plain_text, rich_text, checklist, outlinevisibility optionalprivate (default) or shared. Private notes are only visible to the author. Shared notes are visible to all team membersproject_id optionaltime_spent_seconds optional0Response
{
"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"
} /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
{
"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"
} /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 optionalcontent optionalformat optionalmarkdown, plain_text, rich_text, checklist, outlinevisibility optionalprivate or sharedproject_id optionaltime_spent_seconds optionalResponse
{
"id": "uuid",
"title": "Checkout flow observations (updated)",
"content": "...",
"format": "markdown",
"visibility": "shared",
"time_spent_seconds": 2400,
"updated_at": "2026-03-22T11:00:00Z"
} /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
{ "deleted": true } /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 requiredfile requiredResponse
{
"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), anyvideo/*(mp4, webm, quicktime, mpeg), anyaudio/*(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.
/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 optionaltoday, week, month, or all (default: all)project_id optionalcategory optionaltesting, bug-triage, automation)sort optionalnewest (default), oldest, most_time, least_timeResponse
{
"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
} /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 requiredcategory requiredtesting, bug-triage, automation, review, meeting)duration_minutes requiredproject_id optionalentry_date optionalYYYY-MM-DD format (defaults to today)Response
{
"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"
} /api/time-entries Auth Required Update an existing time tracking entry. Only provided fields are changed.
Request Body
id requireddescription optionalcategory optionalduration_minutes optionalproject_id optionalentry_date optionalYYYY-MM-DD formatResponse
{
"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"
} /api/time-entries Auth Required Delete a time tracking entry. This action cannot be undone.
Request Body
{ "id": "uuid" } Response
{ "deleted": true } Notes
- Time Tracking is an Enterprise plan feature. Free plan users receive a
403 Forbiddenresponse. - 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.
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./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 required403.project_id optionalsince optionalupdated_at > since. Use for delta polling.Response
{
"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"
} /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 requiredproject_id optionalkind optional"recording". Reserved for future types (video, HAR).name optionalduration_ms optionaloptions optionalactions, playwright, video, playwrightAssertions, playwrightWaits, playwrightLocators).actions optionalplaywright optionalstats optional{ actionCount, pageCount }.pages optionalclient_id optionalResponse
{
"resource": { /* same shape as GET /api/resources */ },
"deduped": true // only present when matched on client_id and updated
} /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.
/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)
nameplaywrightnull to clear.project_idactionsoptions, stats, pages/api/resources/:id Auth Required Delete a resource. Owner-only. Also removes any associated file in the user-resources storage bucket.
/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 requiredhistory optional{"role": "user"|"assistant", "content": "..."}. Max 40 entries, 100K total characters.Response
The endpoint content-negotiates the response transport on the Accept header.
{"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.
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
userandassistantroles are accepted. - Off-topic questions (politics, opinions, etc.) are politely redirected.
/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 requireddescription requiredtype optionalui, 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 optionals1 (Blocker), s2 (Critical), s3 (Major, default), s4 (Minor). Legacy values still accepted: critical, high, medium, low.project_id optionalinternal_notes optionalenvironment optional{"browser": "...", "os": "...", "device": "..."}sync_to_jira optionalfalse.attachments optional{"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
{
"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.
/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-8) 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 requiredResponse
{
"analysis": "## Probable Root Cause\n...",
"pushed_at": "2026-03-22T14:30:00Z"
} Response (additional fields)
draftclaude_draft_analysis.critiquenull 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).rebuttals1/critical or s2/high). null otherwise or when the debate chain couldn't adjudicate.challenger_modelgpt-5). null when the step was skipped.adjudicator_modelclaude-opus-4-8). null when the debate path didn't run or the adjudicator call failed.debatedtrue 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_KEYandOPENAI_API_KEYare 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, andclaude_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 Enterprise.
/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 requiredResponse
{
"status": "analyzing"
} Resulting fields on bug_reports
likely_fix_arealikely_fix_area_status = "failed".likely_fix_area_statusanalyzing | done | failed. Null when the analysis has never been attempted.likely_fix_area_generated_atlikely_fix_area.Notes
- No
claude_connectionsrow is required. The endpoint usesANTHROPIC_API_KEYon 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_tokenon the team and (b)github_repoon 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.6 (
claude-sonnet-4-6). Costs are on the platform, not the caller's team. - Available on Enterprise (matches the Developer Notes card gating).
/api/sessions/capture Browser Key Submit session evidence from the browser SDK. Send a project-bound ba_pub_ key in X-BugAgent-Key; the request Origin must exactly match one configured for that key. The workspace and project are derived from the key and cannot be selected in the body. Capture does not require a user login, but replay review and report creation do. Available on Enterprise.
Headers
X-BugAgent-Key requiredba_pub_. Never send a secret ba_live_ server key from browser code.Content-Type requiredapplication/jsonRequest Body
events required{"type": "click"|"navigation"|"error"|"network", "timestamp": "ISO8601", "data": {...}}snapshot optionalconsoleErrors / networkLog optionalscreenshot optionaldom_snapshot / dom_mutations optionalResponse
{
"session_id": "uuid",
"team_id": "uuid",
"project_slug": "storefront",
"dashboard_url": "/dashboard/reports?session=uuid&team=uuid&project=storefront",
"status": "analyzing"
} Notes
- Requires an Enterprise plan. Free plan users receive a
403error. - JSON is capped at 16 MiB, non-screenshot metadata at 2 MiB, and screenshots at 10 MiB decoded.
- Upload an optional recording afterward as multipart form data to
POST /api/sessions/capture-videowith the same browser key and returnedsession_id. Video is capped at 50 MiB and may be attached once. - Per-minute and daily request/byte quotas are enforced across application replicas. A limit returns
429withRetry-After. - Session retention is configurable on Enterprise.
/api/sessions/capture-video Browser Key Attach one browser recording to a session previously created by the same project-bound ba_pub_ key. Send multipart form data with the same exact allowed Origin. The key, workspace, project, and originating capture must all match.
Headers and form fields
X-BugAgent-Key requiredsessions:capture.Content-Length required411.session_id requiredPOST /api/sessions/capture.video requiredvideo/* file, or browser-provided application/octet-stream, up to 50 MiB.Response
{
"video_url": "https://storage.example/session-videos/..."
}Controls and errors
- A session accepts one recording. A second attachment returns
409. - The endpoint allows 6 requests per key and 60 per workspace per minute, plus the browser key's configured daily byte quota.
429includesRetry-After. Distributed rate-limit infrastructure fails closed with503.403covers disallowed origins or browser-key policy;404hides missing and unauthorized sessions.
/api/sessions Auth Required List SDK sessions for the current workspace. Supports pagination and filtering.
Query Parameters
project_id optionalreport_id optionallimit optionaloffset optionalResponse
{
"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
} /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
{
"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"
} /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 requiredreport_id requiredResponse
{
"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.
/api/team-booster Dashboard session, Manager+ Instantly scale your QA team with booster testers. Accounts are provisioned automatically with tester access. Enterprise plan only. Free plan returns 403. You will not be charged until approval has been given.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
team_size | integer | Yes | Number of tester accounts to provision (1–10) |
location | string | Yes | Geographic location for testers (e.g. “US”, “EU”, “APAC”) |
duration | string | Yes | Duration of the engagement (e.g. “1 week”, “1 month”) |
product_url | string | No | URL of the product to be tested |
product_types | string[] | No | Types of products (e.g. ["web app", "mobile app"]) |
tech_levels | string[] | No | Technical levels required (e.g. ["junior", "senior"]) |
budget | string | Yes | Budget for the engagement (e.g. “$500”, “$2000/month”) |
Response
{ "success": true, "created": 5 } Error Responses
{ "error": "Team Booster is available on Enterprise." } /api/admin/changelog Public List all changelog entries, most recent first. Also available as an RSS feed.
Response
{
"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 case plan limits
Retrying a start request with the same external_run_id resumes the existing external run and does not consume another run. Deleting data does not reset monthly run usage. Enterprise test case storage and runs are unlimited, subject to general platform protections.
AI test-case generation, AI tag suggestions, Figma import, and test-case file attachments require Enterprise. The 128 KB Free structured-content bound is separate from Enterprise file attachments. Free can store URL references on a test case. Core API and MCP access remains available on Free within the limits above; direct server enforcement is authoritative.
API-key test catalog contract
Workspace API keys may manage test cases, folders and suites with test_cases:read and test_cases:write. Call GET /api/projects first to discover accessible project UUIDs, slugs, and ticket prefixes. project_id is required for API-key writes that create project-scoped catalog data; the server never silently chooses a project for an API key.
Manual test-run workflows, file attachments, AI generation, Figma import, and other dashboard-only operations still require an interactive session. External agents should execute curated suites through the scoped versioned external-agent contract.
/api/test-cases test_cases:read List test cases for the authenticated user's team. Supports search, filtering by priority, type, and status, sorting, and pagination.
Query Parameters
search optionalpriority optionalcritical, high, medium, lowtype optionalfunctional, regression, smoke, integration, e2e, performance, security, usability, accessibilitystatus optionalactive, draft, deprecatedsort optionalnewest, oldest, name, priority (default: newest)page optionalper_page optionalResponse
{
"test_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
} /api/test-cases test_cases:write Create a new test case. Two template variants: steps (default) — per-step Action + Expected Result grid (pass steps); text — single free-form description of the steps (pass text_content). Both columns can be sent in the same call; the row stores them independently so a tester switching templates later doesn't lose either side's data. Free cases may contain at most 128 KB of structured content.
Request Body
name requireddescription optionalpreconditions optionaltemplate_type optionalsteps (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{ "action": "Click login", "expected": "Login form appears" }. Used when template_type is steps.text_content optionaltemplate_type is text.priority optionalcritical, high, medium (default), lowtype optionalfunctional (default), regression, smoke, integration, e2e, performance, security, usability, accessibilitytags optionalurls optionalestimated_time optionalproject_id required for API keysGET /api/projects. Dashboard sessions retain the legacy optional behavior.Response
{
"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"
} /api/test-cases/:id/attachments Dashboard session Upload an attachment file to a test case. Enterprise is required. Free workspaces can store URL references in the case's urls field instead. Multipart/form-data uses a single file field per request. Files are stored in the test-case-attachments Supabase Storage bucket; metadata is appended to the case's attachments jsonb column.
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
{ "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 } /api/test-cases/:id/attachments/:attachment_id Dashboard session 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
{ "removed": "attachment_id", "attachments_count": 0 } /api/test-cases/:id test_cases:read Get a test case by ID with its full steps and execution history.
Response
{
"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"
} /api/test-cases/:id test_cases:write Update any field of a test case. Only provided fields are changed. The resulting structured content of a Free case may not exceed 128 KB.
Request Body
name optionaldescription optionalpreconditions optionaltemplate_type optionalsteps 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 optionaltext_content optionaltemplate_type is text. Multi-line OK; an empty string normalises to null.priority optionalcritical, high, medium, lowtype optionalstatus optionalactive, draft, deprecatedtags optionalurls optionalestimated_time optionalResponse
{ "id": "uuid", "name": "Updated name", ... } /api/test-cases/:id test_cases:write Permanently delete a test case. Removes it from all suites.
Response
{ "success": true } /api/test-cases/duplicate Dashboard session Duplicate an existing test case including all steps, tags, and metadata. The copy is created with the name prefix "[Copy]".
Request Body
case_id requiredResponse
{ "id": "uuid", "name": "[Copy] Verify checkout with discount code", ... } /api/test-cases/bulk Dashboard session 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 requiredaction requiredset_priority, set_status, set_type, set_folder, add_tags, remove_tags, add_to_suite, pin, unpinparams action-dependentaction:•
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
{ "applied": 47, "skipped": 3, "errors": [] } /api/test-cases/:id/links Dashboard session List traceability links on a single test case — bug reports the case verifies, covers, or relates to. Established via link_test_case_to_bug (MCP) or the Links tab on the case detail page.
Response
{
"links": [
{
"id": "uuid",
"linked_type": "bug_report",
"linked_id": "uuid",
"relation": "verified_by", // "verified_by" | "covers" | "relates"
"created_at": "..."
}
],
"total": 1
} /api/test-cases/review-candidates Dashboard session 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
{
"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
} /api/test-cases/import/figma/request Dashboard session Step 1 of the Enterprise-only Figma zip import flow. Validates inputs, (optionally) creates a new folder, inserts a figma_import_jobs row with status uploading, and returns a signed upload URL the client PUTs the zip to directly. Max 100 MB. Uses the platform Anthropic key server-side — no per-team Claude connection is required.
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
{
"job_id": "uuid",
"folder_id": "uuid | null",
"upload": { "path": "...", "token": "...", "signed_url": "https://..." }
}/api/test-cases/import/figma/start Dashboard session 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
{ "ok": true, "job_id": "uuid" }/api/test-cases/import/figma/:id Dashboard session 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
{
"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"
}
}/api/test-suites test_cases:read List test suites for the authenticated user's team. Returns each suite with its case count and last run status.
Query Parameters
search optionalpage optionalper_page optionalResponse
{
"test_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
} /api/test-suites test_cases:write Create a new test suite to group related test cases.
Request Body
name requireddescription optionalproject_id required for API keysGET /api/projects.Response
{
"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"
} /api/test-suites/:id test_cases:read Get a test suite by ID with its full list of cases in order.
Query Parameters
include_descendants optional1 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
{
"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"
} /api/test-suites/:id test_cases:write Update a test suite's name or description.
Request Body
name optionaldescription optionalResponse
{ "id": "uuid", "name": "Updated Suite Name", ... } /api/test-suites/:id test_cases:write Delete a test suite. Test cases within the suite are not deleted.
Response
{ "success": true } /api/test-suites/:id/cases test_cases:write Add one or more test cases to a suite.
Request Body
case_ids requiredResponse
{ "added": 3, "case_count": 15 } /api/test-suites/:id/cases test_cases:write Remove a test case from a suite.
Request Body
case_id requiredResponse
{ "success": true, "case_count": 14 } /api/test-suites/:id/cases test_cases:write Assign or unassign a case within a suite. The assignee must be an active member of the suite's workspace and have access to its project.
Request Body
case_id requiredassigned_to requirednull to unassign.Returns success, case_id, and the resulting assigned_to value.
/api/test-suites/:id/reorder test_cases:write Reorder the test cases within a suite. Provide the full ordered list of case IDs.
Request Body
ordered_case_ids requiredResponse
{ "success": true } /api/test-suites/reorder test_cases:write 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 requirednull for root-level suites.ordered_suite_ids requiredsort_order is rewritten as the array index for each.Response
{ "success": true, "count": 5 } /api/test-case-folders test_cases:read 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 optionalparent optionalroot — Direct children of the given folder, or top-level folders when set to rootResponse
{
"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
} /api/test-case-folders test_cases:write Create a new folder. Pass parent_folder_id to nest it; folders can nest up to 3 levels deep.
Request Body
name requireddescription optionalparent_folder_id optionalproject_id required for API keysGET /api/projects/api/test-case-folders/reorder test_cases:write 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 requirednull for root-level folders.ordered_folder_ids requiredsort_order is rewritten as the array index for each.Response
{ "success": true, "count": 5 } /api/test-case-folders/:id test_cases:read Get one project-bound folder by UUID. Resources outside the authorized workspace or project are returned as 404.
/api/test-case-folders/:id test_cases:write Rename, describe, or move a folder within its existing project. Supply one or more of name, description, and parent_folder_id. Folder depth is limited to three levels.
/api/test-case-folders/:id test_cases:write Delete a folder hierarchy. The default mode=unfile_cases preserves its cases and moves them to Unfiled. mode=delete_cases also deletes descendant cases and requires owner, admin, or manager access.
/api/test-case-folders/:id/bulk-update test_cases:read Preview a recursive folder bulk action. Returns the source folder and total_case_count across its descendants.
/api/test-case-folders/:id/bulk-update test_cases:write Apply one bounded recursive action to cases in a folder tree: set_folder_color, set_case_color, add_tags, set_priority, or move_cases. The target folder, when used, must be in the same authorized project.
Returns ok: true and the number of affected cases.
External agent execution contract
These versioned endpoints let Hermes or another execution worker run a human-curated suite and return results without taking ownership of bugAgent's test data. Every request is bound to the API key's workspace. The suite determines the project, and its case snapshot is fixed when the run starts.
Create a least-privilege key in Developer settings with test_runs:read and test_runs:write. These scopes authorize only the versioned execution contract below and its corresponding MCP tools.
external_run_id if the start response times out. A matching retry resumes the existing run and does not consume another monthly run. Page through the stored plan, retry result batches with the same case status, and call the abort endpoint if execution cannot continue.On Free, external-agent runs count toward the 10-run UTC calendar-month total and are also capped at 3 per month, 1 active external run, and 10 cases per external plan. Deleting data does not reset monthly run usage. The customer supplies and pays for the target browser, model, and network, and must restrict target access and network egress.
The Hermes Agent integration is a bugAgent-maintained community adapter over this generic contract. The API is agent-neutral and can be used by CI workers and other runtimes. Copy the maintained examples from the public Hermes starter kit.
/api/v1/test-executions test_runs:write Start a new immutable suite execution or resume the matching run after a safe retry. A parent suite includes cases from every descendant suite, deduplicated into a stable order. Returns the first plan page.
Request Body
suite_id requiredexternal_run_id required., _, :, /, and -.name optionalAgent run.agent optionalhermes or ci. Defaults to custom-agent.Example
curl -X POST https://app.bugagent.com/api/v1/test-executions \
-H "Authorization: Bearer ba_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"suite_id": "8b52d61e-0a5a-4e41-b138-2e462f9e4781",
"external_run_id": "hermes:checkout-smoke:2026-07-14T09:00Z",
"name": "Checkout smoke - Hermes",
"agent": "hermes"
}' Response
Returns 201 Created for a new run and 200 OK when the same external_run_id resumes its matching suite. Reusing the ID for another suite returns 409 Conflict.
{
"created": true,
"schema_version": "1.0",
"execution": {
"id": "3f59dd97-cb2b-4f30-98c7-5209a8e9d348",
"external_run_id": "hermes:checkout-smoke:2026-07-14T09:00Z",
"execution_source": "hermes",
"project_id": "2d404ddb-d7ea-439d-95ea-7bf907936398",
"suite_id": "8b52d61e-0a5a-4e41-b138-2e462f9e4781",
"status": "pending",
"results_summary": {
"total": 10, "passed": 0, "failed": 0,
"blocked": 0, "skipped": 0, "untested": 10
}
},
"plan": {
"snapshot": {
"schema_version": "1.0",
"suite": { "id": "8b52d61e-0a5a-4e41-b138-2e462f9e4781", "name": "Checkout smoke" },
"project": { "id": "2d404ddb-d7ea-439d-95ea-7bf907936398", "name": "Storefront" },
"case_count": 10
},
"cases": [
{
"case_id": "9b975f52-9105-46fb-82a4-cbcb05904939",
"origin_suite_id": "8b52d61e-0a5a-4e41-b138-2e462f9e4781",
"plan_order": 0,
"status": "untested",
"case": { "schema_version": "1.0", "name": "Guest checkout succeeds", "steps": [] }
}
],
"next_cursor": null,
"has_more": false
}
} /api/v1/test-executions/:id test_runs:read Read canonical run state and one page of the immutable plan. The response uses the same schema_version, execution, and plan shape returned by the start endpoint.
Path and Query Parameters
id requiredcursor optionalnext_cursor. Omit it for the first page. The cursor is immutable plan_order, not a mutable row offset.limit optionalcurl "https://app.bugagent.com/api/v1/test-executions/3f59dd97-cb2b-4f30-98c7-5209a8e9d348?cursor=99&limit=100" \
-H "Authorization: Bearer ba_live_your_key_here" If has_more is true, pass next_cursor unchanged to retrieve the next page. A run outside the key's workspace returns 404.
/api/v1/test-executions/:id/results test_runs:write Submit 1–200 results atomically. Every case_id must already belong to this run snapshot. Parallel batches serialize on the run, and the canonical summary is recalculated in the same database transaction.
Request Body
results requiredcase_id and status.status requiredpassed | failed | blocked | skippedactual_result optionalnotes optionalduration_seconds optionalcurl -X POST https://app.bugagent.com/api/v1/test-executions/3f59dd97-cb2b-4f30-98c7-5209a8e9d348/results \
-H "Authorization: Bearer ba_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"results": [{
"case_id": "9b975f52-9105-46fb-82a4-cbcb05904939",
"status": "failed",
"actual_result": "Checkout returned HTTP 500 after submitting payment.",
"notes": "Synthetic card; no purchase was created.",
"duration_seconds": 18
}]
}' Response
{
"schema_version": "1.0",
"run_id": "3f59dd97-cb2b-4f30-98c7-5209a8e9d348",
"accepted": 1,
"status": "in_progress",
"results_summary": {
"total": 10, "passed": 0, "failed": 1,
"blocked": 0, "skipped": 0, "untested": 9
}
} Retrying a case with the same status is safe and preserves the first accepted evidence. Submitting a different status for an already tested case returns 409 Conflict. When untested reaches zero, the run status becomes completed automatically.
/api/v1/test-executions/:id/abort test_runs:write Stop an interrupted execution while preserving accepted results and the current summary. The request has no body and is idempotent for an already aborted run. A completed run cannot be aborted.
curl -X POST https://app.bugagent.com/api/v1/test-executions/3f59dd97-cb2b-4f30-98c7-5209a8e9d348/abort \
-H "Authorization: Bearer ba_live_your_key_here" Response
{
"schema_version": "1.0",
"run_id": "3f59dd97-cb2b-4f30-98c7-5209a8e9d348",
"status": "aborted",
"results_summary": {
"total": 10, "passed": 7, "failed": 1,
"blocked": 0, "skipped": 0, "untested": 2
},
"already_stopped": false
} Error behavior
400401403test_runs scope, the workspace/user does not have the Test Cases entitlement, or a Free execution plan exceeds 10 cases.404409429Retry-After for request-rate limits; monthly quotas reset at the next UTC calendar month.422503Retry-After delay.Successful responses include RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset headers. Free allows 30 requests per API key and 60 per workspace per minute for this execution contract; Enterprise allows 120 per key and 600 per workspace per minute.
Dashboard-session API. These unversioned routes power interactive manual test runs and require a signed-in dashboard session. API-key test_runs:* scopes do not authorize these interactive routes. API-key clients and external agents must use the scoped Agent Test Executions contract instead.
/api/test-runs Dashboard session only List test runs for the authenticated user's team. Returns each run with suite name, assignee, and pass/fail summary.
Query Parameters
search optionalstatus optionalin_progress, completedsuite_id optionalpage optionalper_page optionalResponse
{
"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
} /api/test-runs Dashboard session only 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 requiredname requiredassigned_to optionalResponse
{
"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"
} /api/test-runs/:id Dashboard session only 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
{
"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"
} /api/test-runs/:id Dashboard session only Update a test run's name, assignee, or status.
Request Body
name optionaldescription optionalassigned_to optionalstatus optionalarchived 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
{ "id": "uuid", "name": "Updated Run Name", ... } /api/test-runs/:id/results Dashboard session only 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 requiredstatus requiredpassed, failed, blocked, skippedactual_result optionalnotes optionaltest_run_results.notes and surfaced inline on the run carousel; never synced to Jira.bug_report_id optionalnull to explicitly unlink.Response
{ "success": true, "status": "failed", "case_id": "uuid", "notes": "..." } /api/test-runs/:id/results Dashboard session only 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 requiredassigned_to requirednull to unassignResponse
{ "success": true, "case_id": "uuid", "assigned_to": "uuid-or-null" } /api/test-runs/:id/results/attachments Dashboard session only 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 requiredfiles requiredResponse
{
"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 */ ]
} /api/test-runs/:id/results/attachments Dashboard session only 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 requiredattachment_id requiredid returned from the POST)Response
{ "success": true, "attachments": [ /* remaining attachments */ ] } /api/test-runs/:id/complete Dashboard session only Mark a test run as completed. Calculates and stores the final pass rate.
Response
{
"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"
} /api/test-runs/:id/rerun-failed Dashboard session only Create a new test run containing only the failed cases from a completed run. Useful for re-testing after fixes.
Response
{
"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"
} /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 optionalto optionalsuite_id optionalResponse
{
"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
} /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 optionalto optionalproject optionalsuite optionalget_suite_descendants)Response
{
"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
} /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 viatest_run_results.suite_idregressed_cases— most-recent execution failed but the period contained an earlier pass
Query Parameters
from optionalto optionalproject optionalsuite optionalResponse
{
"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 }
]
} /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 optionalto optionalproject optionalsuite optionalget_suite_descendants)Response
{
"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 throughtest_suite_cases. - Trend compares the current pass rate to the same metric over the prior equivalent-length window.
up/downrequire a ±3% change; smaller deltas areflat.newmeans the suite had no runs in the prior window. - Open bugs are
bug_reportslinked from any result in the period whose status is notclosedorresolved.
/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 optionalto optionalproject optionalsuite optionaltest_suite_cases to the suite + descendants (via get_suite_descendants)Response
{
"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_resultsrow withexecuted_atin[from, to]and status notuntested. - Never run = no
test_run_resultsrow in the entire history (catalog hygiene metric, not period-scoped). - Bucket order: rows are evaluated in array order, first match wins — so the
neverbucket is checked before any numeric range. - suite_coverage uses cases linked to the suite via
test_suite_cases(the test-plan relationship), nottest_cases.folder_id(the organizational hierarchy). A case can appear under multiple suites; it counts once per suite. Sorted bycoverage_pctascending; capped at 25 entries.
/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 optionalto optionalproject optionalsuite optionalget_suite_descendants)Response
{
"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_resultswith thistester_id, executed in[from, to], status notuntested. - 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_runswithassigned_to = this userANDcreated_atin the period.runs_assigned_completed= those whose status iscompleted. - 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_idattribution are excluded.
/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 optionalto optionalproject optionalsuite optionalResponse
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.
/api/export/okf Auth Required Export a project’s QA knowledge — bug reports, test cases, automations, and performance, security, and exploratory tests — as an OKF/OQA markdown bundle (the Open Query Agent format used by oqa.ai).
Query Parameters
project optionalResponse
A zip archive with Content-Type: application/zip and a Content-Disposition: attachment header. The filename includes the project slug so saved files stay self-describing: bugagent-okf-acme-app.zip.
/api/export/okf-sync Auth Required Save the per-project GitHub knowledge-sync target — the repo, branch, and path that a project’s OKF/OQA bundle is committed to — and, unless run is false, immediately commit the project’s current OKF/OQA bundle (bug reports, test cases, automations, and performance, security, and exploratory tests, in the oqa.ai format) to that repo via the GitHub Contents API.
Request Body
project requiredrepo requiredowner/repo (e.g. acme/web-app)branch optionalcreateBranch optionaltrue and branch doesn’t exist, create it from the repo’s default branch before committing (default: false)path optionaloqa)run optionaltrue). Set false to save the target only.auto_sync optionaltrue (and the project has a sync target configured), changes to the project’s QA data are pushed to the repo automatically within about a minute by a background worker (default: true)Response
{
"synced": 12,
"failed": 0,
"repo": "acme/web-app",
"branch": "main",
"path": "oqa"
} synced and failed are the counts of bundle files committed and skipped on error; when run is false both are 0. repo, branch, and path echo the resolved target that was saved for the project.
/api/geo-snap Auth Required List saved Geo-Snap screenshots. Filter by country, search by URL, and paginate results.
Query Parameters
search optionalcountry optionalUS, DE, JP)sort optionalnewest (default), oldestpage optionalper_page optionalResponse
{
"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
} /api/geo-snap Auth Required Capture screenshots of a URL from one or more countries. Free plan: 1 country per request, 10 saved screenshots. Enterprise: up to 5 countries per request, unlimited saved screenshots.
Request Body
url requiredcountries required["US", "DE", "JP"]). Free: max 1, Enterprise: max 5Response
{
"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"
}
]
} /api/geo-snap/:id Auth Required Delete a saved Geo-Snap screenshot by ID.
Path Parameters
id requiredResponse
{ "deleted": true } mobile:read, mobile:write, or mobile:run API-key scope. The recorder bootstrap (/api/mobile/record) and script-generation endpoint (/api/mobile/generate-script) require a same-origin dashboard session and are not public automation APIs. Binary replacement uses an internal signed-upload choreography and is dashboard-only until that flow is hardened as a public contract. The runner completion callback is internal and is intentionally omitted./api/mobile/apps mobile:read List uploaded mobile apps. Filter by platform, search by name, and paginate results.
Query Parameters
search optionalplatform optionalandroid or iossort optionalnewest (default), oldest, namepage optionalper_page optionalproject optionalResponse
{
"apps": [
{
"id": "uuid",
"project_id": "uuid",
"name": "MyApp",
"platform": "android",
"package_name": "com.acme.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,
"page": 1,
"per_page": 20
} /api/mobile/apps mobile:write Upload an APK (Android) or IPA (iOS) app binary. Uses multipart/form-data. Max file size: 500 MB.
Request Body (multipart/form-data)
file requiredname requiredplatform requiredandroid or iosversion optional2.1.0)package_name optionalcom.acme.myapp)project_id optionalResponse
{
"id": "uuid",
"project_id": "uuid",
"name": "MyApp",
"platform": "android",
"package_name": "com.acme.myapp",
"version": "2.1.0",
"file_url": "https://storage.bugagent.com/apps/uuid.apk",
"file_size": 48500000,
"created_at": "2026-03-20T10:00:00Z"
} /api/mobile/apps/:id mobile:read Get full details for a mobile app, including automation count.
Path Parameters
id requiredResponse
{
"id": "uuid",
"project_id": "uuid",
"name": "MyApp",
"platform": "android",
"package_name": "com.acme.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"
} /api/mobile/apps/:id mobile:write Update an uploaded mobile app's metadata. Changing its package or simulator target requires authority over every login profile used by linked automations, or an active workspace owner/admin role. The same authorization applies when replacing the app binary through the dashboard.
Path Parameters
id requiredRequest Body
name optionalversion optionalpackage_name optionalResponse
{
"id": "uuid",
"project_id": "uuid",
"name": "MyApp",
"platform": "android",
"package_name": "com.acme.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"
} /api/mobile/apps/:id Dashboard session Replace an uploaded app binary after the dashboard has completed its signed upload. Supply storage_path, and optionally file_size and version. This storage workflow requires an interactive dashboard session and is not an API-key upload contract.
/api/mobile/apps/:id mobile:write Delete an uploaded mobile app and its storage file. Automations referencing this app will be orphaned.
Path Parameters
id requiredResponse
{ "success": true } /api/mobile/automations mobile:read List mobile automation scripts. Filter by app, script type, and status.
Query Parameters
search optionalapp_id optionalscript_type optionalmaestro (YAML), appium (Python), or appium_js (JavaScript)status optionaldraft, active, pausedsort optionalnewest (default), oldest, nameproject optionalpage optionalper_page optionalResponse
{
"automations": [
{
"id": "uuid",
"project_id": "uuid",
"name": "Login Flow Test",
"app_id": "uuid",
"script_type": "maestro",
"execution_mode": "browserstack_maestro",
"status": "active",
"target_devices": [
"Pixel 7",
"Samsung Galaxy S23"
],
"created_at": "2026-03-21T09:00:00Z"
}
],
"total": 1,
"page": 1,
"per_page": 20
} /api/mobile/automations mobile:write Create a mobile automation. Existing action recordings and imported scripts use appium_actions. Recorder-produced action lists are capped at 500 events and must include a resolved, versioned selector decision for every tap; ambiguous taps and obfuscated Android resource-ID placeholders return 422 until the user selects a stable locator or explicitly confirms the recorded coordinate. To run one known-working Maestro YAML flow directly on BrowserStack against an APK or IPA, set script_type to maestro and execution_mode to browserstack_maestro. Its appId must exactly match the linked app’s package or bundle ID; the first validated native flow establishes that ID when the app record does not have one yet.
Request Body
name requiredapp_id requiredscript_type requiredmaestro (YAML), appium (Appium Python), or appium_js (Appium JS)execution_mode optionalappium_actions (default) or browserstack_maestro. Native Maestro requires one self-contained YAML flow with an appId header and command document separated by ---. The appId must match the selected app’s stored package/bundle ID; placeholder IDs are rejected before dispatch. Inline runFlow commands are supported; external runFlow, runScript, and retry file references are rejected. Native commands such as inputRandomText and copyTextFrom, validated runtime expressions such as ${maestro.copiedText}/${output.value}, and bounded ${DATA_*} references pass through validation. It does not use recorded actions or Refine with AI. Secure login profiles may supply only ${USERNAME} and ${PASSWORD}; non-secret variable profiles supply DATA_* values.script requiredtarget_devices optional["Pixel 7", "Samsung Galaxy S23"])recorded_actions optionalelement_bundle.resolution.v = 1; selector mode requires a reviewed resource ID or label, while coordinate mode requires explicit user confirmation.recording_session_id optionalcredential_id optionalcredentialField-tagged inputs. For browserstack_maestro, use a synthetic test account and place ${USERNAME} or ${PASSWORD} only as a complete inputText value; password input must immediately follow a password-like tapOn target. The unchanged placeholder YAML is stored and BrowserStack receives only referenced values at dispatch. Runs can override this default. BrowserStack exposes Maestro environment values unmasked to authorized operators of the BrowserStack account.variable_profile_id optionalbrowserstack_maestro. The YAML may reference uppercase values such as ${DATA_EMAIL}; every referenced key must exist. Only the active profile creator or an active workspace owner/admin may bind it. Scheduled runs inherit this default. Never store credentials, tokens, production personal data, or other secrets in a variable profile.project_id optionalResponse
{
"id": "uuid",
"project_id": "uuid",
"name": "Login Flow Test",
"app_id": "uuid",
"script_type": "maestro",
"execution_mode": "browserstack_maestro",
"script": "appId: com.acme.myapp\n---\n- launchApp\n- tapOn: \"Login\"",
"status": "draft",
"target_devices": [
"Pixel 7",
"Samsung Galaxy S23"
],
"created_at": "2026-03-21T09:00:00Z"
} /api/mobile/automations/import mobile:write Create a runnable mobile automation from a pasted test script, preserving the developer's real locators. Parses Appium (Python or JS), WebdriverIO, Maestro, and Playwright (mobile-web) and stores the parsed steps as recorded_actions that run verbatim (no agent loop). Obfuscated Android resource-ID placeholders are skipped and returned as selector-mapping warnings. Android only. Enterprise plan.
Request Body
name requiredapp_id requiredscript requiredwarnings.target_devices optional["Google Pixel 7"].project_id optionalResponse
{
"id": "uuid", "name": "Login flow", "app_id": "uuid",
"script_type": "appium", "status": "active", "created_at": "…",
"action_count": 7, "dialect": "appium-python", "warnings": []
} Errors: 400 missing field or an iOS app, 404 app not in your workspace, 409 name already exists in the project, 422 no runnable actions parsed (see warnings).
/api/mobile/credentials mobile:read List reusable login profiles for Mobile, Web Automation, and Exploratory AI — non-secret fields only. Stored username/password values are AES-encrypted and never returned. Private profiles owned by another user and profiles in inaccessible projects are omitted; shared profiles are available only to active members with access to the same project.
Query Parameters
project optionalResponse
{
"credentials": [
{ "id": "uuid", "project_id": "uuid", "name": "Admin", "visibility": "shared", "created_at": "2026-07-10T...", "created_by": "uuid", "can_manage": true, "managed_by_workspace": true }
]
} /api/mobile/credentials mobile:write Create a reusable login profile. The username and password are AES-encrypted at rest immediately and can never be read back. Private profiles are creator-only. Shared profiles may be selected by active members with access to the same project; the creator or an active workspace owner/admin may rename, rotate, or delete them. Only the creator can change visibility. Enterprise plan only. Profile names are unique per project.
Request Body
project_id requiredname requiredAdmin. Unique within the project.visibility optionalprivate (default) or shared. Shared profiles may be used by active members with access to the same project.username requiredpassword optionalResponse
{
"credential": { "id": "uuid", "name": "Admin", "project_id": "uuid", "visibility": "private", "created_by": "uuid", "created_at": "2026-07-10T..." }
} Errors: 400 missing/invalid name or username, 404 project not in your team, 409 a profile with that name already exists in the project, 503 credential storage is not configured on the server.
/api/mobile/credentials/:id mobile:write Rotate, rename, or change the visibility of a login profile. Include only the fields you want to change. Supplied secrets are re-encrypted immediately. The active creator may update any profile field. An active workspace owner/admin may rename or rotate a shared profile but cannot change its visibility; private profiles remain creator-only. Enterprise plan only.
Path Parameters
id requiredRequest Body
name optionalusername optionalpassword optionalvisibility optionalprivate or shared.Response
{
"credential": { "id": "uuid", "name": "Admin", "project_id": "uuid", "visibility": "shared", "created_by": "uuid", "created_at": "2026-07-10T..." }
} Errors: 400 nothing to update or invalid field, 403 caller cannot manage the profile or a non-creator attempted to change visibility, 404 profile not found in your team, 409 name already taken in the project.
/api/mobile/credentials/:id mobile:write Soft-delete a login profile. The active creator may delete any profile they created; an active workspace owner/admin may delete a shared profile. Private profiles remain creator-only. The profile row and historical run references remain for audit; future-use defaults are cleared so scheduled runs cannot silently use a deleted secret. Enterprise plan only.
Path Parameters
id requiredResponse
{ "success": true } Errors: 403 caller cannot manage the profile, 404 profile not found in your team (or already deleted).
/api/mobile/variable-profiles mobile:read List readable non-secret variable profiles for one authorized project. Requires mobile:read. Values are reusable synthetic Maestro inputs, not credentials: authorized project members receive the complete values and private visual evidence may display values rendered by the app.
Query Parameters
project required404.Response
{
"profiles": [{
"id": "uuid",
"project_id": "uuid",
"name": "Canadian checkout",
"variables": { "DATA_EMAIL": "qa@example.test", "DATA_REGION": "ca" },
"created_by": "uuid",
"created_at": "2026-07-27T...",
"updated_at": "2026-07-27T..."
}]
} /api/mobile/variable-profiles mobile:write Create a project-scoped non-secret data profile. Requires mobile:write. Keys must match DATA_[A-Z][A-Z0-9_]* within the documented 64-character format. Credential and Maestro-runtime names are reserved. Profiles contain 1–100 string values, each at most 4096 UTF-8 bytes, and at most 65536 bytes total.
Request Body
project_id requiredname requiredvariables required{"DATA_EMAIL":"qa@example.test","DATA_REGION":"ca"}.Response
{ "profile": { "id": "uuid", "project_id": "uuid", "name": "Canadian checkout", "variables": { "DATA_EMAIL": "qa@example.test", "DATA_REGION": "ca" } } } Errors: 400 invalid name, key, value, count, or size; 404 inaccessible project; 409 name already used in the project.
/api/mobile/variable-profiles/:id Auth Required Read one non-secret variable profile and its values. Requires mobile:read, active workspace membership, and project access. Inaccessible and cross-tenant IDs return 404.
Response
{ "profile": { "id": "uuid", "project_id": "uuid", "name": "Canadian checkout", "variables": { "DATA_EMAIL": "qa@example.test", "DATA_REGION": "ca" } } } /api/mobile/variable-profiles/:id Auth Required Rename a profile or replace its complete values object. Requires mobile:write. Only the active profile creator or an active workspace owner/admin may update it; project managers do not receive profile-use authority automatically.
Request Body
name optionalvariables optionalDATA_*, count, and size limits as creation.At least one field is required. The response returns the updated profile and its non-secret values.
/api/mobile/variable-profiles/:id Auth Required Soft-delete a profile. Requires mobile:write and active creator or workspace owner/admin authority. Historical run references remain for audit; automation defaults are cleared, so future immediate runs and schedules no longer inherit the deleted profile.
Response
{ "success": true } /api/mobile/automations/:id mobile:read Get full details for a mobile automation, including app info and recent runs.
Path Parameters
id requiredResponse
{
"id": "uuid",
"project_id": "uuid",
"name": "Login Flow Test",
"app_id": "uuid",
"app_name": "MyApp",
"script_type": "maestro",
"execution_mode": "browserstack_maestro",
"script": "appId: com.acme.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"
} /api/mobile/automations/:id mobile:write Update a mobile automation script or its metadata. The execution mode is immutable. Updates to native Maestro scripts are revalidated as a single YAML flow; recorded actions remain rejected for native Maestro mode. Action-replay updates are capped at 500 events and reject unresolved recorder taps with 422. Same-project credential_id and non-secret variable_profile_id defaults may be set or cleared when the YAML references their supported placeholders.
Path Parameters
id requiredRequest Body
name optionalscript optionalstatus optionaldraft, active, or pausedtarget_devices optionalrecorded_actions optionalappium_actions automations. Maximum 500 events. Recorder-linked automations require every tap to have a resolved versioned selector decision.credential_id optionalvariable_profile_id optionalDATA_* key must exist.Response
{
"id": "uuid",
"project_id": "uuid",
"name": "Login Flow Test",
"app_id": "uuid",
"app_name": "MyApp",
"script_type": "maestro",
"execution_mode": "browserstack_maestro",
"script": "appId: com.acme.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"
} /api/mobile/automations/:id mobile:write Delete a mobile automation. Fails if the automation has active schedules — delete schedules first.
Path Parameters
id requiredResponse
{ "success": true } /api/mobile/automations/:id/versions mobile:read List up to 100 retained script versions for an authorized mobile automation. Sensitive input values are not returned.
/api/mobile/automations/:id/versions mobile:write Restore a retained version while atomically preserving the current script as a new history entry.
Request Body
version requiredupdated_at required409./api/mobile/automations/:id/visual-baseline mobile:read Read baseline metadata. Add ?item=N to receive a short-lived redirect to one authorized baseline image.
/api/mobile/automations/:id/visual-baseline Dashboard session Create a visual baseline from a passed run using JSON { "run_id": "uuid" }, or upload one image as multipart form data in file. Uploads are limited to 10 MB. This evidence-management write currently requires an interactive dashboard session.
/api/mobile/automations/:id/visual-baseline mobile:write Remove the saved baseline and disable visual comparison for the automation.
/api/mobile/runs mobile:read List mobile test runs. Filter by automation and status.
Query Parameters
automation_id optionalstatus optionalqueued, running, passed, failed, error, archivedsort optionalnewest (default), oldestproject optionalpage optionalper_page optionalResponse
{
"runs": [
{
"id": "uuid",
"automation_id": "uuid",
"execution_mode": "browserstack_maestro",
"status": "passed",
"device": "Pixel 7",
"os_version": "14",
"duration_ms": 45000,
"created_at": "2026-03-22T08:00:00Z"
}
],
"total": 1
} /api/mobile/runs mobile:run Trigger a mobile test run on BrowserStack. appium_actions automations use the existing Appium replay executor. browserstack_maestro automations upload and execute the stored YAML through BrowserStack’s native Maestro API for both Android and iOS.
Request Body
automation_id requireddevice optionalPixel 7, iPhone 15 Pro). Defaults to the automation’s first target device.os_version optional14 for Android 14, 17.2 for iOS)credential_id optionalinputText references to ${USERNAME}/${PASSWORD}, preserve validated built-in Maestro runtime expressions and selected DATA_* values, and reject unsupported unresolved environment variables. Values are never returned by bugAgent, although BrowserStack receives referenced Maestro environment values unmasked. Credentialed native BrowserStack Maestro runs retain private video, a representative end-of-run JPEG frame, any YAML takeScreenshot output, filtered logs, real step names, and detailed failures for 30 days. Before persistence, bugAgent removes exact known credential values and recognized generic secret patterns. This filtering reduces disclosure risk but does not guarantee detection of every possible secret displayed by the tested app. If credential decryption or redaction context is unavailable, or sanitization cannot be proven safe, detailed text is withheld while status and available visual evidence remain. Run diagnostics require workspace and project authorization; media uses five-minute signed links. Auto-created bugs and Slack/email notifications remain generic and do not copy credentialed diagnostics. Credentialed Appium action runs retain structural metadata only. Use a synthetic least-privilege account, mask password fields, and avoid displaying secrets elsewhere on screen.variable_profile_id optionalnull to run without one, or send a same-project profile UUID to override it. Every ${DATA_*} key referenced by the YAML must exist. Exact known profile values receive best-effort filtering from persisted textual evidence, but transformed, partial, encoded, or app-derived forms may remain and authorized private video and screenshots may display values rendered by the app. Profiles must contain only synthetic non-secret data.Response
{
"run_id": "uuid",
"status": "queued"
} /api/mobile/runs/:id mobile:read Get results for an authorized mobile test run. Uncredentialed Appium runs include Appium/device/network logs; uncredentialed native Maestro runs include BrowserStack video, a representative end-of-run frame, any YAML takeScreenshot output, the flow result, Maestro command logs, device/network logs, and duration. Credentialed native BrowserStack Maestro runs retain private visual evidence, filtered logs, real step names, detailed failures, status, duration, counts, and BrowserStack identifiers for 30 days. Exact known credential values and recognized generic secret patterns are removed before text is persisted. Because this filtering cannot guarantee identification of every possible secret displayed by the tested app, detailed text is withheld whenever credential decryption or redaction context is unavailable or sanitization cannot be proven safe; status and available visual evidence remain. Workspace membership and project access are enforced for diagnostics. Private media is delivered with five-minute signed links. Auto-created bugs and Slack/email notifications remain generic and do not copy credentialed diagnostics. Credentialed Appium action runs return structural metadata and BrowserStack identifiers only.
Path Parameters
id requiredResponse
{
"id": "uuid",
"project_id": "uuid",
"automation_id": "uuid",
"execution_mode": "browserstack_maestro",
"status": "passed",
"device": "Pixel 7",
"os_version": "14",
"duration_seconds": 45,
"results": {
"source": "browserstack_maestro",
"framework": "maestro",
"steps": [
{
"name": "Open dashboard",
"status": "passed",
"duration": 1400
}
],
"passed": 1,
"failed": 0,
"total": 1
},
"video_url": "https://app.bugagent.com/api/mobile/runs/uuid/artifact?name=video.mp4",
"screenshots": [
"https://app.bugagent.com/api/mobile/runs/uuid/artifact?name=screenshot-1.jpg"
],
"logs_text": "=== MAESTRO LOG ===\nFlow completed",
"credential_text_retained_at": "2026-03-22T08:00:45Z",
"created_at": "2026-03-22T08:00:00Z",
"completed_at": "2026-03-22T08:00:45Z"
} /api/mobile/runs/:id mobile:write 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"archived"/api/mobile/runs/:id/bug-report mobile:write Create or link a bug report for an authorized mobile run whose status is failed or error. This idempotent operation requires mobile:write. Repeated requests return the report already linked to the run. Credentialed runs produce a generic report without private logs, screenshots, or video; authorized users can review that evidence on the run detail page.
Path Parameters
id requiredResponse
{
"bug_report_id": "uuid",
"bug_short_id": "TEST-BA-227",
"existing": false
} Returns 404 for a missing or unauthorized run and 409 when the run did not fail or its source automation is no longer available.
/api/mobile/runs/:id/artifact mobile:read Return a five-minute signed redirect for an artifact already referenced by the authorized run. Use the exact name from a run response, such as video.mp4 or screenshot-1.jpg. Arbitrary storage paths are rejected.
/api/mobile/devices mobile:read List available BrowserStack real devices for mobile testing. Optionally filter by platform.
Query Parameters
platform"android" or "ios". Omit to get both./api/mobile/refinements mobile:run Start a Refine with AI job for a recorded mobile automation, or resume its active one. An agent iteratively hardens the recorded tour into a self-healing, device-certified script — layering in resilient locators (accessibility id, resource id, semantic labels) and proving it with repeated green runs on a real device. One active refinement per automation; jobs count against a monthly plan quota. Android apps only for now. Enabled per workspace — a workspace without Refine with AI access receives 403 (contact us to turn it on).
Request Body
automation_id requireddevice required"Google Pixel 8")os_version optionalbudget optional{ model_loops, device_runs, escalations }. Values are clamped to platform caps (defaults: 12 / 8 / 1; device runs never exceed 25).verbosity optionalnarrated (default) or condensedintent_goal optional"Book a hotel room for two nights"). Guides the agent so it certifies against your goal, not just whether the flow still runs. Trimmed and capped at 2000 characters.intent_waypoints optional["Sign in", "Add item to cart", "Reach checkout"]). Blank entries are dropped; up to 20 are kept.step_expectations optional{ step, expect }, where step is the 0-based index into the recorded actions and expect is what should be true at that step (e.g. { step: 3, expect: "Cart badge reads 1" }). Up to 50 are kept. During certification each expectation is checked against a screenshot captured at that step; the result is recorded, and with intent_strict a confident mismatch blocks the run from certifying.intent_strict optionaltrue, certification fails if the script can't reach the goal, every waypoint, or a step expectation. Defaults to false (advisory): the goal, waypoints, and step expectations guide the agent and are checked by the certifier, but a run that misses one can still certify.Response
201 Created with { refinement }, or 200 OK with { refinement, resumed: true } when the automation already has an active job. Returns 503 when Refine with AI is temporarily unavailable (maintenance or no refinement worker configured in the environment) — no job is created; retry later.
422 when the recorded tour signs in using a saved login profile (a credential field was captured during recording). Refine with AI cannot inject a login profile, so login tours are not refined on this path — certify a login flow with a native Maestro (YAML) run, which injects the profile securely.
/api/mobile/refinements/:id mobile:read Get a refinement's state plus its event log (transcript, stage changes, run reports). Events are ordered by a monotonic cursor for replay.
Query Parameters
since optionalcursor). Defaults to 0 (full history, capped at 500 events per page).Response
200 OK with { refinement, events, cursor }.
/api/mobile/refinements/:id/messages mobile:run Send a chat message to the refining agent. Messages queue and are delivered at the next safe point in the loop — you can send anytime while the job is active. Secrets in the message (API keys, tokens, private keys, password=-style assignments) are automatically masked before the message is stored or reaches the agent.
Request Body
text requiredResponse
201 Created with { event_id, redacted, redactions } — redacted is true when a secret was masked, and redactions is a by-kind count of what was masked (e.g. { aws_key: 1 }). 400 if the message is empty or exceeds 2000 characters. 409 if the refinement has finished. 429 if the per-refinement message cap is reached or messages are sent too quickly.
/api/mobile/refinements/:id/interrupt mobile:run Stop an active refinement. The job pauses (keeping its draft and history) and any held device session is released. Resume later via POST /api/mobile/refinements.
Response
200 OK with { refinement }; 409 if already finished.
/api/mobile/refinements/:id/approve mobile:run Approve the refined draft: freezes it as an immutable script version (with full provenance) and copies it into the automation's recorded_actions so future runs use it.
Response
200 OK with { refinement, script_version }; 400 when there is no draft; 409 while the job is still running.
/api/automations/schedules Auth Required List all web automation schedules for the team. Returns cron expression, timezone, device, notification settings, and next run time.
/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), login_profile_id, notify_on_fail (none/email/slack/both), notify_email, slack_channel_id. A profile UUID selects an authorized project-scoped identity for this schedule; null explicitly runs without a profile and does not inherit later automation-default changes. If the automation uses reusable authenticated sessions, each selected profile maintains a separately encrypted, target- and execution-scoped session. Supports BrowserStack Live via browserstack: true + bs_browser, bs_os, bs_os_version — same matrix as POST /automations/runs: Node scripts get desktop + real Android + real iPhone; Python scripts get desktop only. See the Live-browser device list on that endpoint for device names.
/api/mobile/schedules mobile:read List scheduled mobile test runs with cron expression, timezone, and notification settings.
Response
{
"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
} /api/mobile/schedules mobile:write Create a scheduled mobile test run. Runs are dispatched to the configured devices on the cron schedule and inherit the automation’s current login-profile and non-secret variable-profile defaults. Private login profiles require their active creator; shared login profiles require active same-project access. Non-secret variable profiles retain their creator-or-owner/admin policy. Change the automation default to change future scheduled inputs; schedules do not store a separate variable-profile override.
Request Body
automation_id requiredcron_expression required0 9 * * 1-5 for weekdays at 9am)timezone optionalUTC)devices requirednotify_on_fail optionalnone (default), email, slack, bothResponse
{
"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"
} /api/mobile/schedules/:id mobile:read Get one mobile schedule, including its automation name, cron expression, timezone, devices, notification settings, enabled state, and next run time. The schedule must belong to a workspace the caller can access.
Path Parameters
id requiredResponse
{
"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"
} /api/mobile/schedules/:id mobile:write Update a mobile test schedule. Only the active schedule creator or an active workspace owner/admin may change its timing, devices, notification settings, or enabled state.
Path Parameters
id requiredRequest Body
cron_expression optionaltimezone optionaldevices optionalnotify_on_fail optionalenabled optionalResponse
{
"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"
} /api/mobile/schedules/:id mobile:write Delete a mobile test schedule. Only the active schedule creator or an active workspace owner/admin may delete it.
Path Parameters
id requiredResponse
{ "success": true } /api/performance/tests Auth Required List all performance test configurations for the current team.
Response
{ "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": "..." }] } /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. Enterprise only.
Body Parameters
name requiredurl requireddevice optionaldesktop (default), mobile, tabletvirtual_users optionalduration optionalperf_threshold optionalauto_create_bug optionalapp_id optionalPOST /mobile/apps). Required for mobile performance testsmobile_devices optional["Pixel 7", "iPhone 15 Pro"]). Each device creates one run and consumes 1 monthly creditmobile_thresholds optionalcpu_max (%), memory_peak (MB), fps_min, battery_drain_max (%), launch_time_max (ms). Exceeding any threshold triggers a bug reportResponse
{ "test": { "id": "uuid", "name": "Homepage Perf", "url": "https://example.com", ... } } /api/performance/tests/:id Auth Required Get a single performance test configuration by ID.
Path Parameters
id required/api/performance/tests/:id Auth Required Update a performance test configuration. Only include fields you want to change.
Path Parameters
id requiredBody Parameters
name optionalurl optionaldevice optionalvirtual_users optionalperf_threshold optionalauto_create_bug optional/api/performance/tests/:id Auth Required Delete a performance test configuration and all its associated runs.
Path Parameters
id requiredResponse
{ "success": true } /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 requiredResponse
{ "run": { "id": "uuid", "status": "running", "test_id": "uuid", "created_at": "..." } } /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/api/performance/runs/:id/pdf Auth Required Download a PDF report for a performance run. Includes Lighthouse scores, Core Web Vitals, load test summary with latency percentiles, and test metadata.
Path Parameters
id requiredResponse
Returns application/pdf with an attachment filename derived from the performance test and run.
/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 requiredResponse
{ "success": true, "status": "archived" } /api/performance/runs/:id/archive Auth Required Unarchive a previously archived performance test run, restoring it to the default listing.
Path Parameters
id requiredResponse
{ "success": true, "status": "completed" } /api/performance/usage Auth Required Check monthly performance test usage against plan limits. Performance testing is Enterprise-only. Customer limits: Free=0, Enterprise=unlimited.
Response
{ "used": 1, "limit": -1, "remaining": -1, "plan": "enterprise", "period_start": "2026-03-01", "period_end": "2026-03-31" } /api/security/scans Auth Required Create a security scan configuration. Requires an Enterprise plan.
Request Body
name requiredscan_type requiredweb or mobiletarget_url web onlyapp_id mobile onlyscan_depth optionalquick (default, ~30s), regular (~5min), deep (up to 30min)auth_config optional{ login_url, username, password } for authenticated web scanningauto_create_bug optionalmin_severity_for_bug optionalcritical, high (default), medium, low/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 requiredscan_depth optionalResponse
{ "id": "run-uuid", "status": "queued" } /api/security/runs/{id} Auth Required Get scan run status and results. Poll this endpoint while status is queued or running.
Response (completed)
{
"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
} /api/security/domains Auth Required List verified domains for the team. Use POST to add a domain and receive a DNS TXT verification token.
/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/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 the Enterprise monthly security scan cap of 20. Admin users bypass the cap.
Request Body
scan_id requiredcron_expression required0 9 * * 1-5 for weekdays at 9am)timezone optionalUTC)notify_on_fail optionalnone (default), email, slack, bothnotify_email optionalslack_channel_id optionalResponse
{
"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"
} /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 optionaltimezone optionalenabled optionalnotify_on_fail optionalnotify_email optionalslack_channel_id optional/api/security/schedules/:id Auth Required Delete a security scan schedule. Does not affect the parent scan config or any completed runs.
/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/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. Enterprise only.
repostringGitHub repo in owner/repo formatpr_numberintegerPull request number/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.
/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.
/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.
/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 Claudeauto_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 findingsmin_severity_for_bugstring?Minimum severity for auto-bug: critical, high, medium, low/api/code-review/usage Auth Required Check code review usage. Returns reviews used and plan info. AI code review is Enterprise-only; unlimited on Enterprise.
/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).
/api/github/webhooks Auth Required List all GitHub webhooks registered for the team. Returns webhook ID, repo, active status, and creation date.
/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.
/api/github/webhooks Auth Required Remove a GitHub webhook from a repository. Requires repo. Deletes the webhook from both GitHub and the bugAgent database.
/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.
/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.
/api/explorations Auth Required Create a new exploration config. Requires name and target_url. Optional: exploration_context (JSONB with focus_areas, instructions, auth_config containing login_url and username, exclude_paths, max_depth, test_data), write-only auth_password, project_id, auto_create_bug, min_severity_for_bug, agent_count (1–10, clamped to plan limit; Enterprise: max 10), agent_strategies (array of strategy IDs: happy_path, edge_case, security, accessibility, error_path, performance, mobile, data_integrity, navigation, custom). Saved passwords are encrypted and are never returned by REST or MCP responses.
/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.
/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.
/api/explorations/usage Auth Required Check monthly Exploratory AI usage. Exploratory AI is Enterprise-only; Enterprise: unlimited.
/api/explorations/trends Auth Required Get exploration history and trends. Returns run history, finding trends by type and severity over time, screenshot comparisons between consecutive runs, and delta vs previous run. Query params: exploration_id (optional filter), days (7/30/90, default 30).
/api/explorations/schedules Auth Required List exploration schedules for the signed-in workspace.
/api/explorations/schedulesAuth RequiredCreate an exploration schedule. Requires exploration_id and cron_expression, with optional email or Slack failure notification settings.
/api/explorations/schedulesAuth RequiredUpdate an authorized exploration schedule, including its enabled state, cron expression, timezone, and notification settings.
/api/explorations/schedulesAuth RequiredDelete an authorized exploration schedule.
Compliance Center
Automated SOC2, ISO 27001, and GDPR compliance evidence collection, config drift monitoring, access reviews, and security event tracking. Enterprise 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 }.
Suggestions & Recommendations
Use API keys over JWTs
API keys may be created with an expiry and are simpler for server-to-server or agent integrations. Use the shortest practical expiry and rotate keys periodically. 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, MCP provides tool discovery and structured agent workflows. REST and MCP overlap substantially but are not identical; choose the interface whose documented contract contains the operation you need.
Rotate API keys periodically
For zero-downtime rotation, create a second key with the same least-privilege scopes, update and verify every consumer, then revoke the old key. POST /keys/:id/regenerate invalidates the old secret immediately and is appropriate only when coordinated downtime is acceptable.
Subscribe to the changelog
Stay up to date with API changes via the changelog page or RSS feed.