# Application Builds API
Source: https://docs.qa.tech/api-reference/application-builds
Upload and manage mobile app builds for testing
The Application Builds API enables you to upload mobile app builds (APK/IPA files) for testing with QA.tech. Use these endpoints to integrate mobile app build uploads into your CI/CD pipelines and automation workflows.
## When to Use This API
* **CI/CD integration** – Automatically upload builds after successful compilation
* **Mobile app testing** – Upload iOS and Android builds for automated testing
* **Version management** – Track and test different build versions
This API is for **mobile applications** (iOS and Android) only. For web
applications, use environment URLs directly in the [Start Run
API](/api-reference/runs/start-test-run) or [Chat API](/api-reference/chat).
## Supported File Types
| Platform | File Types |
| -------- | ---------- |
| Android | `.apk` |
| iOS | `.ipa` |
Android App Bundles (`.aab`) are not supported directly. Convert them to
`.apk` first — see [Mobile App Testing](/test-features/mobile-app-testing) for
instructions.
## Authentication
All endpoints require Bearer token authentication. Create your API key in the QA.tech dashboard: **Organization Settings → API Keys**. The key is shown only once, at creation. See the [API Introduction](/api-reference/introduction#authentication) for details.
## Upload Workflow
Uploading a build is a two-step process, plus the file transfer itself:
1. **[Get Build Upload URL](/api-reference/application-builds/get-build-upload-url)** – Request a presigned URL and a `buildToken` for your file.
2. **Upload the file** – Send the raw file bytes with an HTTP `PUT` request to the presigned `uploadUrl`. No `Authorization` header is needed — the URL is presigned.
3. **[Create Application Build](/api-reference/application-builds/create-application-build)** – Register the uploaded file as a build, using the `buildToken` from step 1. The response includes the build's short ID (e.g. `build_abc123`).
This flow allows efficient direct uploads to cloud storage without passing the file through the API server. See the linked reference pages for full request, response, and error details.
The presigned URL is temporary and valid for about 2 hours. Upload the file
promptly after requesting the URL.
## Using Builds in Test Runs
After creating a build, reference its short ID as the environment's `applicationBuildShortId` in an application override when [starting a run](/api-reference/runs/start-test-run) or [creating a chat conversation](/api-reference/chat). The run or chat session will then test against that specific build.
## Related
* [Mobile App Testing](/test-features/mobile-app-testing) – Mobile testing concepts
* [Start Run API](/api-reference/runs/start-test-run) – Run tests against uploaded builds
* [Applications API](/api-reference/applications) – List applications
* [API Introduction](/api-reference/introduction) – Authentication and ID reference
# Create application build
Source: https://docs.qa.tech/api-reference/application-builds/create-application-build
/api-reference/api.json post /v1/applications/{applicationShortId}/builds
Step 2 of 2 for attaching a mobile build: registers a file already uploaded via `application_build_upload_url`. Pass the `buildToken` returned by that tool (after you PUT the file to its `uploadUrl`). Returns `applicationBuildShortId` — use it as `environment.applicationBuildShortId` inside an `applications[]` override in `start_run_with_test_cases` / `start_run_with_test_plan`, or an `applicationOverrides[]` override in `create_chat`, to run/chat against this build.
# Get build upload URL
Source: https://docs.qa.tech/api-reference/application-builds/get-build-upload-url
/api-reference/api.json post /v1/applications/{applicationShortId}/builds/upload-url
Step 1 of 2 for attaching a mobile build (.apk/.ipa) to a run. Returns a presigned `uploadUrl` and a `buildToken`. Next, upload the raw build file to `uploadUrl` with an HTTP PUT request (request body = the raw file bytes, `Content-Type` = the file's MIME type such as `application/octet-stream`; no auth header — the URL is presigned), then call `create_application_build` with the same `buildToken`.
# Applications API
Source: https://docs.qa.tech/api-reference/applications
List applications and their environments programmatically
Retrieve applications and their environments from your project. Use these endpoints to discover application and environment short IDs for use with other API endpoints like [Start Run](/api-reference/runs/start-test-run) and [Chat](/api-reference/chat).
You can also find Application and Environment Short IDs in the QA.tech
dashboard: **Settings → Applications & Envs**. The short IDs (e.g.
`app_gXeBl2`, `env_aB3xY9`) are displayed in the UI and can be copied using
the three-dot menu (⋮).
## Endpoints
For full request, response, and error details, see the generated reference pages:
* [List Applications](/api-reference/applications/list-applications) – all applications in your project, including their kind (web or mobile)
* [List Application Environments](/api-reference/applications/list-application-environments) – the environments configured for a specific application
For mobile applications, the iOS/Android platform is set per build when
[uploading application builds](/api-reference/application-builds), not on the
application itself.
## Authentication
All endpoints require Bearer token authentication. Create your API key in the QA.tech dashboard: **Organization Settings → API Keys**. The key is shown only once, at creation. See the [API Introduction](/api-reference/introduction#authentication) for details, including how organization-scoped keys pass `projectShortId`.
## Typical Workflow
A common pattern is to discover IDs dynamically instead of hardcoding them:
1. Call [List Applications](/api-reference/applications/list-applications) to find the application's `shortId`.
2. Call [List Application Environments](/api-reference/applications/list-application-environments) with that ID to find the environment you want to test against (for example, the first non-production environment, using the `isProduction` flag).
3. Pass the discovered IDs as application/environment overrides when [starting a run](/api-reference/runs/start-test-run) or [creating a chat conversation](/api-reference/chat).
This is especially useful in CI/CD pipelines, where the target environment may vary per branch or deployment.
## Related
* [Start Run API](/api-reference/runs/start-test-run) – Use application/environment IDs to run tests
* [Chat API](/api-reference/chat) – Use application overrides in chat conversations
* [Applications and Environments](/core-concepts/applications-and-environments) – Learn about the application model
* [API Introduction](/api-reference/introduction) – Authentication and ID reference
# List application environments
Source: https://docs.qa.tech/api-reference/applications/list-application-environments
/api-reference/api.json get /v1/applications/{applicationShortId}/environments
List environments for a specific application
# List applications
Source: https://docs.qa.tech/api-reference/applications/list-applications
/api-reference/api.json get /v1/applications
Lists the applications under test in the project the API key is bound to.
# Chat API
Source: https://docs.qa.tech/api-reference/chat
Interact with the QA.tech AI Chat Assistant programmatically
The Chat API enables programmatic interaction with QA.tech's AI Chat Assistant. Create conversations, send messages, start change reviews, and poll for responses—all via REST API. This is ideal for integrating QA.tech's AI capabilities into your automation workflows, project management systems, or custom tooling.
## When to Use This API
* **Automated test creation** – Send context from your system to create tests without opening the QA.tech UI
* **PR/Change reviews** – Automatically trigger AI-powered change reviews from CI/CD pipelines
* **Custom integrations** – Build buttons or workflows in your project management tools that interact with QA.tech Chat
* **AI-assisted QA workflows** – Let your automation systems communicate with QA.tech's AI assistant
For simpler chat interactions, consider using the [CLI chat
command](/cli/commands/chat) which handles polling automatically: `qatech chat
"Create a login test"`
## Authentication
All endpoints require Bearer token authentication. Create your API key in the QA.tech dashboard: **Organization Settings → API Keys**. The key is shown only once, at creation. See the [API Introduction](/api-reference/introduction#authentication) for details.
## Endpoints
For full request, response, and error details, see the generated reference pages:
* [Start Chat Conversation](/api-reference/chat/start-chat-conversation) – Create a conversation and send the first message
* [Start Change Review Chat](/api-reference/chat/start-change-review-chat) – Create a conversation that reviews a pull request or raw code changes
* [Send Chat Message](/api-reference/chat/send-chat-message) – Send a follow-up message to an existing conversation
* [Get Chat Conversation](/api-reference/chat/get-chat-conversation) – Retrieve conversation metadata and messages
## How Conversations Work
Chat processing is **asynchronous**. Creating a conversation or sending a message returns `202 Accepted` immediately with an empty `messages` array — the assistant's reply is not included. To read the reply, poll [Get Chat Conversation](/api-reference/chat/get-chat-conversation) until the most recent `assistant` message reaches the `COMPLETED` status. We recommend polling every **2-5 seconds**.
Assistant responses typically complete within 10-60 seconds depending on the
complexity of the request. Test creation and execution may take several
minutes.
You can target a specific application environment for the conversation — for example a preview deployment or an uploaded mobile build — by passing application overrides when creating it. See the [Start Chat Conversation](/api-reference/chat/start-chat-conversation) reference for the override format.
## Change Reviews
[Start Change Review Chat](/api-reference/chat/start-change-review-chat) creates a conversation that initiates an AI-powered review of code changes. It supports two modes:
* **PR mode** – Provide a pull request URL (GitHub or GitLab) and QA.tech fetches the changes
* **Raw changes mode** – Provide a change description and a git diff directly, useful when the changes are not in a hosted PR
Both modes accept optional extra context to help the AI understand the changes, and require application overrides specifying which environment to review against.
For GitHub pull requests (`mode: "pr"`, `vcsProviderId: "github"`), an explicit change-review request also creates or updates the **QA.tech / PR Review** check on the PR. This applies even when **Auto-run on PRs** is disabled in the GitHub App integration. See [PR Review check on GitHub](/configuration/github-app#pr-review-check-on-github).
## Related
* [AI Chat Assistant](/core-concepts/ai-chat-assistant) – Learn about QA.tech's AI assistant
* [API Introduction](/api-reference/introduction) – Authentication and ID reference
* [Start Run API](/api-reference/runs/start-test-run) – Trigger test runs programmatically
# Get chat conversation
Source: https://docs.qa.tech/api-reference/chat/get-chat-conversation
/api-reference/api.json get /v1/chat/{chatConversationShortId}
Get conversation metadata and recent messages (newest first).
Use `limit` to control how many messages are returned (default 20). To detect when an
assistant reply is finished, poll until the most recent `assistant` message has
`status: 'COMPLETED'` (or `CANCELLED`/`FAILED`).
# Send chat message
Source: https://docs.qa.tech/api-reference/chat/send-chat-message
/api-reference/api.json post /v1/chat/{chatConversationShortId}
Send a chat message to an existing conversation.
Returns 202 immediately. Poll `get_chat_conversation` until the latest
assistant message has `status: 'COMPLETED'` to read the reply. Wait at least 1s between polls.
# Start change review chat
Source: https://docs.qa.tech/api-reference/chat/start-change-review-chat
/api-reference/api.json post /v1/chat/change-review
Create a new chat conversation and start an autonomous change review from either a PR URL or raw change input.
# Start chat conversation
Source: https://docs.qa.tech/api-reference/chat/start-chat-conversation
/api-reference/api.json post /v1/chat
Create a new chat conversation and send its first message.
This is the recommended way to author tests. The QA.tech agent explores the site, grounds tests in what actually exists, and sets up login and config dependencies, so the tests are reliable rather than flaky. Prefer it over `create_test_case` unless you have already verified the flow exists and gathered the details that tool requires.
Returns 202 immediately with the created conversation. Processing is asynchronous: poll
`get_chat_conversation` until the latest assistant message has
`status: 'COMPLETED'` to read the reply. Wait at least 1s between polls.
# Exporting Test Cases
Source: https://docs.qa.tech/api-reference/exporting-test-cases
Export your test case definitions, including steps, via the API or CLI
Export all test case definitions from your project, including names, goals, expected results, and step-by-step instructions. This is useful for migrating to another test management tool (for example Jira with Xray or Zephyr), keeping external backups, or building custom reports.
## What Gets Exported
The [List Test Cases](/api-reference/test-cases/list-test-cases) endpoint returns published test cases. Each test case includes its name, goal, expected result, labels, enabled state, classification, and the most recent run status. When you pass `includeSteps=true`, each test case also includes a `steps` array with step-by-step instructions.
See the [List Test Cases reference](/api-reference/test-cases/list-test-cases) for the full response schema.
Steps are opt-in via the `includeSteps` query parameter to keep default
responses small. Test cases that are defined by a goal only (without a step
breakdown) return an empty `steps` array.
## Export via the API
Pass `includeSteps=true` to include the full step definitions:
```bash theme={null}
curl "https://api.qa.tech/v1/test-cases?includeSteps=true&limit=1000" \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
The endpoint returns up to 1000 test cases per request. If your project has more, page through the results with `offset`:
```bash theme={null}
curl "https://api.qa.tech/v1/test-cases?includeSteps=true&limit=1000&offset=1000" \
-H "Authorization: Bearer YOUR_API_TOKEN"
```
See the [API Introduction](/api-reference/introduction#authentication) for how to create an API token. With an organization-scoped key, also pass `projectShortId` as a query parameter.
## Export via the CLI
The [qatech CLI](/cli/overview) wraps the same endpoint:
```bash theme={null}
qatech test-cases --include-steps --json > test-cases.json
```
See [qatech test-cases](/cli/commands/test-cases) for all options.
## Converting to CSV
Many test management importers expect one row per step. This `jq` one-liner converts the JSON export into that shape:
```bash theme={null}
qatech test-cases --include-steps --json | jq -r '
["Test Case ID", "Test Case Name", "Step Number", "Step Instruction", "Step Expected Result"],
(.testCases[] | . as $tc | .steps // [] | to_entries[] |
[$tc.id, $tc.name, (.key + 1), .value.instruction, (.value.expectedResult // "")])
| @csv' > test-case-steps.csv
```
For a one-row-per-test-case shape with numbered steps in a single column:
```bash theme={null}
qatech test-cases --include-steps --json | jq -r '
["Test Case ID", "Name", "Goal", "Expected Result", "Steps"],
(.testCases[] |
[.id, .name, (.goal // ""), (.expectedResult // ""),
([.steps // [] | to_entries[] | "\(.key + 1). \(.value.instruction)"] | join("\n"))])
| @csv' > test-cases.csv
```
## Related
* [List Test Cases](/api-reference/test-cases/list-test-cases) - Full endpoint reference
* [qatech test-cases](/cli/commands/test-cases) - CLI command reference
* [API Introduction](/api-reference/introduction) - Authentication and ID reference
# Get feature
Source: https://docs.qa.tech/api-reference/features/get-feature
/api-reference/api.json get /v1/features/{featureId}
Fetch detail for a single feature, including pages it spans and actions that belong to it
# List features
Source: https://docs.qa.tech/api-reference/features/list-features
/api-reference/api.json get /v1/features
List features in the project's feature graph. Features are LLM-clustered groupings of related UI actions discovered during crawling, and describe what the product can do.
# Get outbound IPs
Source: https://docs.qa.tech/api-reference/infrastructure/get-outbound-ips
/api-reference/api.json get /v1/outbound-ips
Get the current outbound IP addresses QA.tech test runners use, for firewall rules and allowlists. No authentication required.
# API Introduction
Source: https://docs.qa.tech/api-reference/introduction
Learn how to interact with QA.tech programmatically using our REST API
The QA.tech REST API allows you to programmatically control test runs, create test cases, check results, and integrate QA.tech into your CI/CD pipelines and automation workflows.
## What Can You Do?
* **Start test runs** programmatically from CI/CD pipelines or scripts
* **Create and list test cases** via API for importing tests or generating them dynamically
* **Check run status** and retrieve test results
* **Rerun tests** (all or a subset) from a previous run
* **Chat with AI assistant** to create tests and review changes via API
* **List applications and environments** for dynamic test configuration
* **Create remote tunnels** to test local environments
* **Upload mobile app builds** for iOS and Android testing
* **Get outbound IPs** for firewall allowlisting
## Quick Start
Here's a complete example to start a test run:
```bash theme={null}
curl -X POST https://api.qa.tech/v1/run \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"testPlanShortId": "pln_abc123"}'
```
See [Start Test Run](/api-reference/runs/start-test-run) for all request options, including application/environment overrides.
**What you need:**
1. **API Token** - Created in **Organization Settings → API Keys** (the key is shown only once, at creation)
2. **Test Plan Short ID** - Prefixed ID from your test plan (e.g. `pln_abc123`). Alternatively pass `testCaseIds` to run specific test cases instead of a plan
3. **Other short IDs** as needed - Application, Environment, Config, Device Preset, Scenario (see table below)
## Authentication
All API endpoints (except a few public ones such as [Get Outbound IPs](/api-reference/infrastructure/get-outbound-ips) and the status badge endpoints) require Bearer token authentication. Include your API token in the `Authorization` header:
```bash theme={null}
Authorization: Bearer YOUR_API_TOKEN
```
**Where to find your API token:**
1. Navigate to **Organization Settings → API Keys**
2. Create a new API key
3. Copy the key when it is shown — it will not be displayed again
API keys can be **project-scoped** or **organization-scoped**. With a
project-scoped key the project is inferred from the token, so you can omit
`projectShortId`. With an organization-scoped key you must pass
`projectShortId` (e.g. `proj_abc123`) in the request body or query string of
project-scoped endpoints.
## Understanding Different IDs
QA.tech uses **prefixed short IDs** for most API resources. The prefix indicates the resource type. Some fields (e.g., dependency test case IDs in Create Test Case and Rerun endpoints) still use UUIDs and are documented on their respective pages:
| ID Type | Format | Where to Find | Used In API | Example |
| -------------------------- | -------------------------- | --------------------------------------------- | ----------------------------------------------- | ----------------- |
| **Project Short ID** | `proj_` + alphanumeric | Project URLs, Settings | `projectShortId` (required for org-scoped keys) | `proj_abc123` |
| **Test Plan Short ID** | `pln_` + alphanumeric | Test plan URLs, Settings | `testPlanShortId` in request body | `pln_abc123` |
| **Application Short ID** | `app_` + alphanumeric | Settings → Applications & Envs | `applicationShortId` in applications array | `app_gXeBl2` |
| **Environment Short ID** | `env_` + alphanumeric | Settings → Applications & Envs → Environments | `environment.shortId` in applications array | `env_aB3xY9` |
| **Config Short ID** | `cfg_` + alphanumeric | Settings → Configs | `configShortIds` in test-cases body | `cfg_xyz789` |
| **Device Preset Short ID** | `preset_` + alphanumeric | Settings → Device Presets | `devicePresetShortId` in applications array | `preset_abc123` |
| **Scenario Short ID** | `scenario_` + alphanumeric | Scenarios | `scenarioShortId` in test-cases body | `scenario_abc123` |
| **Chat Conversation ID** | `chat_` + alphanumeric | Chat API response | `chatConversationShortId` in chat endpoints | `chat_abc123` |
| **Application Build ID** | `build_` + alphanumeric | Build API response | `applicationBuildShortId` in environment | `build_abc123` |
### Finding Application and Environment Short IDs
Application and Environment Short IDs are shown in the UI with their `app_` and `env_` prefixes.
Navigate to **Settings → Applications & Envs** in your project
The Application Short ID (e.g. `app_gXeBl2`) appears in the applications
table. Use the three-dot menu (⋮) to **Copy Short ID**
Under **Settings → Applications & Envs → \[Select Application]**, the
Environment Short ID (e.g. `env_aB3xY9`) appears in the **Environments**
section. Use the copy action to copy it
## Next Steps
* **[Start a Run](/api-reference/runs/start-test-run)** - Trigger test runs programmatically
* **[Get Run Status](/api-reference/runs/get-run)** - Wait for test completion in CI/CD pipelines
* **[Rerun Tests](/api-reference/runs/rerun-run)** - Rerun all or failed tests from a previous run
* **[Create Test Cases](/api-reference/test-cases/create-test-case)** - Create tests via API
* **[List Test Cases](/api-reference/test-cases/list-test-cases)** - Retrieve test cases from your project
* **[Chat API](/api-reference/chat)** - Interact with the AI assistant programmatically
* **[Applications API](/api-reference/applications)** - List applications and environments
* **[Remote Tunnels API](/api-reference/remote-tunnels)** - Create tunnels to test local environments
* **[Application Builds API](/api-reference/application-builds)** - Upload mobile app builds
* **[Get Outbound IPs](/api-reference/infrastructure/get-outbound-ips)** - Retrieve IPs for firewall allowlisting
* **[View OpenAPI Spec](/api-reference/api.json)** - Complete API reference with all endpoints
## Base URL
All API requests are made to:
```
https://api.qa.tech/v1
```
## Migration from Legacy API
If you used the legacy API format:
* **Old:** `https://app.qa.tech/api/projects/{projectUuid}/runs`
* **New:** `https://api.qa.tech/v1/run` (project is inferred from your API key)
The old format may still work temporarily but is **deprecated**. Key changes in the current API:
* No `projectUuid` in the path; the project is determined by your API token
* All IDs use **prefixed short IDs** (e.g. `pln_abc123`, `app_gXeBl2`) instead of UUIDs where applicable
* Base URL is `https://api.qa.tech/v1`
Migrate to the new base URL and request format when possible.
# Get issue
Source: https://docs.qa.tech/api-reference/issues/get-issue
/api-reference/api.json get /v1/issues/{shortId}
Fetches a single issue by its short ID, including its tags and the recent run test cases where it was detected (newest first). Use the occurrences' `runTestCaseShortId` with `get_run_test_case` to inspect what the agent saw.
# List issues
Source: https://docs.qa.tech/api-reference/issues/list-issues
/api-reference/api.json get /v1/issues
Lists issues QA.tech has found in the project, deduplicated across the runs where they recur and sorted by most recent occurrence (newest first). Defaults to `ACTIVE` issues; filter by `severity`, `status`, or a `since`/`until` window. Each issue includes its severity, status, type, a help URL, timestamps, and a deep link. Pass a returned `shortId` to `get_issue` for the run test cases that triggered it.
# Create knowledge item
Source: https://docs.qa.tech/api-reference/knowledge/create-knowledge-item
/api-reference/api.json post /v1/knowledge
Add an item to the project's knowledge base and index it for the QA.tech agents. Use `text` for documentation notes, `link` to crawl and index a URL, or `memory` for agent-maintained observations. PDF and icon items are managed in the dashboard.
# Delete knowledge item
Source: https://docs.qa.tech/api-reference/knowledge/delete-knowledge-item
/api-reference/api.json delete /v1/knowledge/{knowledgeItemId}
Delete a knowledge item and remove it from the agents' knowledge index
# Get knowledge item
Source: https://docs.qa.tech/api-reference/knowledge/get-knowledge-item
/api-reference/api.json get /v1/knowledge/{knowledgeItemId}
Fetch a single knowledge item including its full text content
# List knowledge items
Source: https://docs.qa.tech/api-reference/knowledge/list-knowledge-items
/api-reference/api.json get /v1/knowledge
List the project's knowledge base items: text notes, crawled links, uploaded PDFs, agent memories and UI icons. Returns summaries without content; fetch a single item for its full content.
# Update knowledge item
Source: https://docs.qa.tech/api-reference/knowledge/update-knowledge-item
/api-reference/api.json patch /v1/knowledge/{knowledgeItemId}
Update a text, link or memory knowledge item. Changing content or url re-indexes the item; pdf and icon items are managed in the dashboard.
# Get project metrics
Source: https://docs.qa.tech/api-reference/metrics/get-project-metrics
/api-reference/api.json get /v1/metrics
Prometheus text exposition format for the project bound to the API key.
# Create project
Source: https://docs.qa.tech/api-reference/projects/create-project
/api-reference/api.json post /v1/projects
Create a new project in the organization the API key belongs to. Requires an organization-scoped API key with write scope.
# List projects
Source: https://docs.qa.tech/api-reference/projects/list-projects
/api-reference/api.json get /v1/projects
Lists the projects in the organization the API key belongs to. Organization-scoped keys see every project; project-scoped keys see only their own. Returned `shortId`s can be passed as `projectShortId` to any project-scoped tool to target that project.
# Get release check
Source: https://docs.qa.tech/api-reference/release-checks/get-release-check
/api-reference/api.json get /v1/release-checks/{shortId}
Fetch the compiled release check report by its short ID. Poll until `status` is `completed`.
# Start release check
Source: https://docs.qa.tech/api-reference/release-checks/start-release-check
/api-reference/api.json post /v1/release-checks
Start a release check: an orchestrated pre-release report that runs your
regression suite and/or an autonomous exploratory change review, then
compiles the results into a single report.
Request at least one of a regression suite (`regression`) or an exploratory
change review (`prUrl` or `changes`). All testing and analysis runs on
QA.tech — it does not consume the caller's tokens or credits.
Returns 202 immediately. Poll `get_release_check` until `status` is
`completed` (or `failed`) to read the compiled report.
# Remote Tunnels API
Source: https://docs.qa.tech/api-reference/remote-tunnels
Create and manage secure tunnels to expose local environments
The Remote Tunnels API enables you to programmatically create secure tunnels that expose local ports via Cloudflare. This allows QA.tech to access locally-running applications for testing without deploying them to a public server.
## Prerequisites
Before using the Remote Tunnels API, you need:
1. **cloudflared** – Cloudflare's tunnel client must be installed on your machine or CI runner
```bash theme={null}
# macOS
brew install cloudflared
# Linux (Debian/Ubuntu)
curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared.deb
# See https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/
```
2. **Your application running locally** – The tunnel exposes local ports, so your app must be running
## Tunnel Expiration
Tunnels expire **4 hours** after creation. The `expiresAt` field in the
response indicates when the tunnel will be automatically torn down. For
long-running development, you'll need to create a new tunnel when the current
one expires.
## When to Use Remote Tunnels
* **Local development testing** – Test your local environment without deploying
* **CI/CD pipelines** – Expose ephemeral test servers during build processes
* **Preview environments** – Create temporary public URLs for testing
* **Firewall-protected environments** – Access applications behind corporate firewalls
For CLI-based tunnel management, see the [tunnel
command](/cli/commands/tunnel) documentation. The CLI provides a simpler
interface for common tunnel operations.
## Authentication
All endpoints require Bearer token authentication. Create your API key in the QA.tech dashboard: **Organization Settings → API Keys**. The key is shown only once, at creation. See the [API Introduction](/api-reference/introduction#authentication) for details.
## Endpoints
For full request, response, and error details, see the generated reference pages:
* [Create Remote Tunnel](/api-reference/remote-tunnels/create-remote-tunnel) – Expose one or more local ports; returns the tunnel's public hostnames and a `tunnelToken`
* [List Remote Tunnels](/api-reference/remote-tunnels/list-remote-tunnels) – All tunnels for your project, including expired ones
* [Get Remote Tunnel Status](/api-reference/remote-tunnels/get-remote-tunnel-status) – Live Cloudflare health status of a tunnel
* [Delete Remote Tunnel](/api-reference/remote-tunnels/delete-remote-tunnel) – Tear down a tunnel and its DNS records
## Tunnel Lifecycle
1. **Create the tunnel** via [Create Remote Tunnel](/api-reference/remote-tunnels/create-remote-tunnel), specifying the local ports to expose. The response contains the public hostnames mapped to each port, a `tunnelToken`, and the `runnerId` used in subsequent calls.
2. **Start the cloudflared daemon** with the returned token:
```bash theme={null}
cloudflared tunnel run --token YOUR_TUNNEL_TOKEN
```
The tunnel remains active as long as cloudflared is running and the tunnel has not expired (4-hour maximum lifetime).
3. **Check health** with [Get Remote Tunnel Status](/api-reference/remote-tunnels/get-remote-tunnel-status) before pointing tests at the tunnel.
4. **Use the public URLs** as environment overrides when [starting a run](/api-reference/runs/start-test-run) or [creating a chat conversation](/api-reference/chat).
5. **Clean up** with [Delete Remote Tunnel](/api-reference/remote-tunnels/delete-remote-tunnel) when you're done.
For simpler tunnel management, consider using the [CLI tunnel
command](/cli/commands/tunnel) which handles cloudflared automatically:
`qatech tunnel start --port 3000`
## Hostname Format
Each exposed port gets a public HTTPS hostname based on the tunnel's `runnerId`:
* Single port without `subdomain`: `r-{runnerId}.quack.run`
* Multiple ports without `subdomain`: `r-{runnerId}-p{localPort}.quack.run`
* With `subdomain`: `r-{runnerId}-{subdomain}.quack.run`
## Related
* [Tunnel CLI Command](/cli/commands/tunnel) – CLI interface for tunnel management
* [SSH Tunnel](/configuration/ssh-tunnel) – Alternative tunneling via SSH
* [Start Run API](/api-reference/runs/start-test-run) – Use tunnel URLs as environment overrides
* [API Introduction](/api-reference/introduction) – Authentication and ID reference
# Create remote tunnel
Source: https://docs.qa.tech/api-reference/remote-tunnels/create-remote-tunnel
/api-reference/api.json post /v1/remote-tunnels
Create a new remote tunnel that exposes local ports via Cloudflare.
# Delete remote tunnel
Source: https://docs.qa.tech/api-reference/remote-tunnels/delete-remote-tunnel
/api-reference/api.json delete /v1/remote-tunnels/{runnerId}
Tear down a remote tunnel and its DNS records
# Get remote tunnel status
Source: https://docs.qa.tech/api-reference/remote-tunnels/get-remote-tunnel-status
/api-reference/api.json get /v1/remote-tunnels/{runnerId}/status
Get the live Cloudflare health status of a remote tunnel
# List remote tunnels
Source: https://docs.qa.tech/api-reference/remote-tunnels/list-remote-tunnels
/api-reference/api.json get /v1/remote-tunnels
List remote tunnels for the authenticated project
# Create rule
Source: https://docs.qa.tech/api-reference/rules/create-rule
/api-reference/api.json post /v1/rules
Create a rule: a standing instruction injected into a QA.tech agent's context. Agent rules support filters to target specific applications, labels, scenarios or URL paths.
# Delete rule
Source: https://docs.qa.tech/api-reference/rules/delete-rule
/api-reference/api.json delete /v1/rules/{ruleId}
Delete a rule so it is no longer injected into the agent's context
# List rules
Source: https://docs.qa.tech/api-reference/rules/list-rules
/api-reference/api.json get /v1/rules
List the project's rules: standing instructions injected into the QA.tech agents. Filter by consumer to see rules for the chat assistant, the test agent, or PR review.
# Update rule
Source: https://docs.qa.tech/api-reference/rules/update-rule
/api-reference/api.json patch /v1/rules/{ruleId}
Update a rule's title, content, filters or sort order. Pass null on a filter field to clear it.
# Get run
Source: https://docs.qa.tech/api-reference/runs/get-run
/api-reference/api.json get /v1/run/{shortId}
Fetches a run by its short ID. Pass `testCases: "all"` or `"failed"` to include nested test-case results, and `issues` to include detected issues (`all` or a category such as `accessibility`; omitted by default). Use `get_run_issues` to fetch only the issues.
# Get run issues
Source: https://docs.qa.tech/api-reference/runs/get-run-issues
/api-reference/api.json get /v1/run/{shortId}/issues
Lists issues detected during a run, deduplicated and aggregated across its test cases. Returns every issue type by default; pass `issueType` to filter to one category (e.g. `accessibility`). Each issue includes its type, severity, tags, a help URL, and the test cases where it was found.
# Get run test case
Source: https://docs.qa.tech/api-reference/runs/get-run-test-case
/api-reference/api.json get /v1/run-test-case/{shortId}
Fetches a single run test case (one test's execution within a run) by its short ID — the `` in a `/results/test/` URL. Returns the goal and success criteria, the evaluator's reasoning, the result and error classification, durations, and a deep link plus best-effort media URLs. Use `get_run_test_case_trace` for the step-by-step action trace and `get_test_case_history` for the pass/fail trend.
# Get run test case trace
Source: https://docs.qa.tech/api-reference/runs/get-run-test-case-trace
/api-reference/api.json get /v1/run-test-case/{shortId}/trace
Returns the agent's step-by-step action trace for a run test case: per step the reasoning, the tools it called with their arguments, the observed result, and a screenshot. Paginated via `limit`/`offset` so large traces don't overflow token limits.
# Get test case history
Source: https://docs.qa.tech/api-reference/runs/get-test-case-history
/api-reference/api.json get /v1/run-test-case/{shortId}/history
Returns the recent pass/fail history and durations for the test case behind a run test case short ID, across its most recent runs — for flaky-vs-consistent analysis.
# List runs
Source: https://docs.qa.tech/api-reference/runs/list-runs
/api-reference/api.json get /v1/run
Lists runs for the project, newest first. With no filters it returns the 20 most recent runs across all time; pass `since`/`until` (ISO 8601) to scope to a window such as the last 24 hours. Returned `shortId`s work directly with `get_run` and `rerun_run`.
# Rerun run
Source: https://docs.qa.tech/api-reference/runs/rerun-run
/api-reference/api.json post /v1/run/{shortId}
Reruns a previous run. Pass `failedOnly: true` to rerun only failed/skipped/errored cases, or `projectTestCaseIds` to rerun a specific subset.
# Start test run
Source: https://docs.qa.tech/api-reference/runs/start-test-run
/api-reference/api.json post /v1/run
Execute a run for a given project. Supports API or GITHUB trigger. When using GITHUB trigger, provide actor, branch, commitHash, commitMessage, and repository. Use applications to override environment or device preset per run.
# Get example status badge
Source: https://docs.qa.tech/api-reference/status-badge/get-example-status-badge
/api-reference/api.json get /v1/badge-example.svg
Get a sample status badge as SVG (no token). For live project badges use GET /v1/badge.svg.
# Get status badge
Source: https://docs.qa.tech/api-reference/status-badge/get-status-badge
/api-reference/api.json get /v1/badge.svg
Get status badge as SVG. Authenticate with the token query parameter (not the API Bearer key). Project is resolved from the token.
# Create test case
Source: https://docs.qa.tech/api-reference/test-cases/create-test-case
/api-reference/api.json post /v1/test-cases
Create a test case in the project. It is created in draft and a burn-in run starts automatically.
IMPORTANT: gather context first, or the test will be flaky. This tool records the test exactly as you describe it. It does NOT explore the site to confirm the flow exists, so a vague or unverified goal produces low-value, flaky tests. Before calling, make sure you have:
- Verified the functionality, pages, and UI labels actually exist in the target environment. Do not guess or assume.
- A `goal` describing a single concrete user flow in plain UI terms, using generic data rather than specific entity names. Specific entities change over time and cause flakiness.
- An `expectedResult` stating a UI-observable outcome a user can see on screen.
- Prerequisites in place: a login/auth dependency via `resumeFromDependencyProjectTestCaseId` when the flow needs authentication, plus the right `configShortIds`.
- Checked existing tests with `list_test_cases` to avoid duplicates.
If you have not gathered this context, prefer `create_chat` instead. The QA.tech chat agent crawls the site, grounds the test in what actually exists, sets up login and config dependencies, and verifies the test runs. That is how reliable tests get created. Use this tool directly only when you already know the flow exists and have the details above.
# Get test case
Source: https://docs.qa.tech/api-reference/test-cases/get-test-case
/api-reference/api.json get /v1/test-cases/{id}
Fetches a test case's authored configuration by its ID (UUID, as returned by `list_test_cases`): goal, expected result, steps, start URL path, dependencies, labels, enabled state, and scenario. This is the authored config the agent executes — for a specific run's outcome use `get_run_test_case` instead.
# List test cases
Source: https://docs.qa.tech/api-reference/test-cases/list-test-cases
/api-reference/api.json get /v1/test-cases
Lists test cases for the project, optionally filtered by application, labels, or enabled state.
# Update test case
Source: https://docs.qa.tech/api-reference/test-cases/update-test-case
/api-reference/api.json patch /v1/test-cases/{id}
Updates a test case's authored configuration. Patch semantics: only the fields you provide change; pass `null` on nullable fields (`startUrlPath`, `scenarioShortId`, `resumeFromDependencyProjectTestCaseId`) to clear them.
The edit takes effect immediately as a new published revision. To de-hardcode a test that 404s on a stale deep link, clear `startUrlPath` and set `resumeFromDependencyProjectTestCaseId` to a test that creates the entity it needs — it then starts from that test's end state with fresh data.
# BankID
Source: https://docs.qa.tech/applications/se-bank-id
## What is BankID
BankID is a digital identification system widely used in countries like Sweden, Norway, and Finland. It allows individuals to securely prove their identity online and authorize electronic transactions.
## How to test BankID
There are two approaches for handling BankID in your QA.tech tests: **stubbing** BankID in your testing environment, or **manual authentication** using the live screen stream.
***
## Option 1: Stubbing BankID (Recommended)
Stubbing gives you fully automated tests without manual intervention. This requires changes to your application code by your development team.
From BankID's official documentation:
> **How do I test my BankID implementation? How about automation?**
>
> Testing can't be automated. Passwords/Security codes have to be manually entered in the BankID clients. We recommend building a so-called test stub that simulates the BankID service web service. It can also be used to perform load tests on your services.
> [https://developers.bankid.com/support](https://developers.bankid.com/support)
### What is stubbing?
Stubbing replaces the BankID integration with a placeholder that automatically grants access in your testing environment. For example, you accept a specific personnummer like `199001011234` and always grant that user access. This should only be permitted in a *testing* environment, never in production.
On the client:
```javascript theme={null}
if (environment === 'testing') {
// Show stub UI for BankID
} else {
// Show real BankID
}
```
On the server:
```javascript theme={null}
if (environment === 'testing') {
if (personalNumber === '199001011234') // Send an auth success response
else // Send an auth failed response
} else {
// Auth using real BankID
}
```
***
## Option 2: Manual Authentication
If you can't stub BankID, you can manually complete the authentication yourself. Since QA.tech live streams the browser screen during test execution, you can watch the test and scan the BankID QR code in real time.
Create a dedicated login test that waits for you to authenticate, and then reuse that login state across all your other tests via [dependencies](/core-concepts/dependencies). You only need to authenticate **once per test run**.
Create a test with instructions like:
```
Go to the login page and click "Log in with BankID".
A QR code will appear. Wait up to 10 minutes for the login to complete.
After login, verify that you are on the logged-in dashboard.
```
The long wait gives you enough time to find the running test, open the live stream, and scan the QR code with your phone.
Start the test run. The login test will navigate to the BankID login page and wait.
Find the running login test in the QA.tech dashboard and open its live screen stream. You'll see the BankID QR code on screen. Open the BankID app on your phone and scan the QR code shown in the stream.
Confirm the login in your BankID app. The test will detect that the login succeeded and continue.
### Reusing Login State Across Tests
Set up your other tests to reuse the authenticated session using **Resume From** dependencies. This way the BankID login only happens once.
```mermaid theme={null}
graph TB
subgraph "Browser Session"
T1["Login with BankID (manual QR scan)"]
T2["Test: Check account settings (Resume From Login)"]
T3["Test: Submit a form (Resume From Login)"]
T1 -->|Resume From| T2
T1 -->|Resume From| T3
end
```
See [Test Dependencies](/core-concepts/dependencies) for details on how Resume From works.
The login test's browser state is cached for up to 6 hours. If your BankID
session lasts that long, you won't need to re-authenticate between consecutive
test runs.
# Creating Tests
Source: https://docs.qa.tech/best-practices/creating-tests
Learn how to create effective test cases in QA.tech and write tests that the AI agent can execute reliably.
## When to Create Tests
QA.tech works best when tests target **features that already exist** in your application. Since the AI agent crawls your site and builds a knowledge graph to design test cases, it relies on the feature being built and accessible.
### Testing Features That Don't Exist Yet
If you ask the chat to create a test for something that isn't built yet (e.g., "Go to the Admin page and add a user" when there's no Admin page), the agent will either:
* Spend time searching for a feature it can't find
* Report that it doesn't know how to complete the task
This isn't a productive use of your time or the agent's capabilities.
### Recommended Workflow for New Features
Instead of creating tests before the feature exists, **document what should be tested in your ticket or PR description**:
When creating a Linear, Jira, or other ticket for a new feature, include
acceptance criteria that describe what should be tested. For example: -
"User can navigate to Admin page and add a new user" - "New user receives
invitation email" - "Admin can see the new user in the user list"
When you open the PR, include testing requirements in the description. The
[GitHub App](/configuration/github-app) agent reads your PR and uses this
context to create appropriate tests.
When the PR is published and your preview environment is ready, our agent
analyzes the PR changes and acceptance criteria, then creates and runs tests
for the new functionality. See [GitHub App for PR
Reviews](/configuration/github-app) for details.
This workflow means you spend time describing *what* to test (which you'd do
anyway for acceptance criteria), and the agent handles *how* to test it once
the feature is ready.
## What Should Be a Test?
A good example of a test is something a user would like to do, or achieve. A user story or user journey should probably map 1:1 to a test case.
#### Test Examples
* Log in with correct credentials
* Log in with incorrect credentials
* Create a new task
* Edit a task's due date
## Creating a Test
You can create test cases through the UI with AI-generated suggestions, or conversationally through the AI Chat Assistant.
### Create Tests via UI
Click the **"Add Test Case"** button to open the test creation modal with two options:
**Suggested tests** - QA.tech continuously crawls your application to discover testable features and interactions. These appear as AI-generated suggestions you can select and add to your project. Click "Analyze my site" to trigger a new crawl if you want fresh suggestions.
**Create your own test** - Describe what you want to test in natural language. The AI agent will understand your goal and attempt to generate the test steps automatically.
Key fields:
* **Name**: Clear, descriptive name (e.g., "Create admin user and verify access")
* **Goal**: What the test should accomplish in natural language
* **Expected Result** (optional): What success looks like
* **Dependencies**: Configure test execution order (see [Test Dependencies](/core-concepts/dependencies) for WAIT\_FOR and RESUME\_FROM)
* **Configurations**: Add required data like credentials or test accounts
* **Advanced**: Agent selection and other settings
### Choosing an AI Agent
When creating a test, you can select which AI agent executes it. Click the **Advanced** section in the test creation modal or test settings to see agent options.
| Agent | Speed | Best For |
| :----------------------------- | :------- | :---------------------------------------------- |
| **Claude Haiku 4.5** (default) | Fastest | Most tests - recommended for day-to-day testing |
| Claude Sonnet 4.5 | Moderate | Complex scenarios requiring deeper reasoning |
**Claude Haiku 4.5** is the default for all new tests. It provides the fastest execution while handling most testing scenarios effectively - form filling, navigation, verification, and standard user flows.
**When to consider Sonnet:** If a test consistently fails with Haiku on complex multi-step reasoning or edge cases, try switching to Sonnet for that specific test.
Most users never need to change the default agent. QA.tech selects Haiku 4.5
because it offers the best balance of speed and capability for typical testing
workflows.
### Create Tests via AI Chat Assistant
The [AI Chat Assistant](/core-concepts/ai-chat-assistant) provides a conversational, exploratory approach to test creation:
**Natural conversation** - Describe your testing needs in plain English. Ask for multiple tests, request specific coverage areas, or iterate on suggestions through back-and-forth conversation.
**Upload context** - Drag and drop specification documents, design files, or requirements (PDFs, images, text files) directly into the chat. The AI uses this context to generate more accurate, relevant tests.
**Safe experimentation** - Tests generated in chat aren't committed to your project until you explicitly click "Add Selected Tests". You can review, refine, or discard them without affecting your team. Uploaded files remain isolated to that chat conversation only.
Example prompts:
* "Generate 5 tests for the checkout flow"
* "What areas of my product should I cover with test cases first?"
* "Create a test that validates login and checks the user profile page"
See [AI Chat Assistant](/core-concepts/ai-chat-assistant) for more examples.
### Review and Refine
After creating a test, the AI agent automatically attempts to execute it and generate test steps. Click the **review button** to inspect the results:
**Left sidebar** - View and edit the goal, expected result, and generated steps. The Settings tab lets you configure dependencies, add required configurations, or adjust agent settings.
**Right panel** - Inspect the execution trace showing exactly what the agent did. This helps you verify the test behaves as intended.
Update steps as needed and click **"Save & Run"** to test your changes. You can stop execution at any time with the **"Stop"** button.
### Refine Tests via Chat
The fastest way to fix and improve tests: describe changes in the [AI Chat Assistant](/core-concepts/ai-chat-assistant), review the diff, and run immediately.
**Fix after failures** - When a test fails, stay in chat and describe the fix. No context switching - see the failure, fix it, validate.
**Build iteratively** - Create a rough test, watch it run, refine through conversation. The AI remembers what you both just saw.
**Bulk refinement** - Describe what you want across your test suite and let the AI handle the details. It reads your tests, identifies which ones to change, and proposes edits for each.
#### Example Prompts
| You say... | What happens |
| ---------------------------------------------- | --------------------------------------- |
| "Change step 3 to wait for the spinner first" | AI proposes single step edit |
| "Also verify the success message appears" | AI adds verification to current test |
| "Make this resume from my Login test" | AI updates dependency |
| "Add email verification to all checkout tests" | AI finds and edits multiple tests |
| "Create tests for returns similar to checkout" | AI reads your tests, generates new ones |
The AI already knows your test suite and remembers your conversation. You can
say "that test" or "where it failed" - no need to be overly specific. Describe
what you want in plain language and let the AI figure out the details.
**Only for existing tests:** Editing works on tests that have been created.
For suggestions you haven't added yet, keep describing changes and the AI will
regenerate the suggestion.
## Activating and Organizing Tests
After creating and reviewing your test, you'll want to activate it and organize it within your project.
### Activate Your Test
Tests are created in draft mode so you can review and refine them before they run. When you're ready, click the **"Activate"** button to enable the test and make it part of your active test suite.
If your test has dependencies that are also in draft mode, QA.tech will prompt
you to activate them together to ensure proper execution order.
Once activated, the button changes to **"Convert to draft"** - use it if you need to temporarily disable the test for maintenance or updates.
### Organize with Scenarios
On the Test Cases page, you can drag and drop tests to organize them into Scenario groups. Scenarios help you:
* Keep related tests together (e.g., "Checkout Flow", "User Management")
* Create logical test groupings for better organization
* Get a clear overview during test execution
* Set up test dependencies within related workflows
## Writing Effective Tests
### Writing a Good Goal
The goal is the main objective of the test. The agent uses this to build steps and adapt when your application changes. Focus on describing **what to do**, not what to validate (use expected result for that).
**Good goal examples:**
* Search for 'Chair', navigate to a product and add it to the cart
* Invite a new member with Admin role to the project
* Open the customer support chat and send a message
**Keep your goals:**
* **Action-oriented** - Start with verbs like "Create", "Search", "Navigate", "Add"
* **Specific** - Include exact details (product name, user role, button labels)
* **Focused** - Describe actions to take, not validation criteria
### Writing a Good Expected Result
The expected result defines what the agent should verify at the end of the test. Describe what should be visible or observable when the test completes successfully.
**Good expected result examples:**
* The page should contain a user avatar
* A success message appears and the user is redirected to the product list
* The user receives an email with a password reset link
**Keep your expected results:**
* **Observable** - Focus on things that can be verified visually or through system responses
* **Specific** - Include exact elements, messages, or states to check
* **Outcome-focused** - Describe the end state, not how to get there
Keep tests to 10 steps or less. If you need more steps, create a new test with
a [dependency](/core-concepts/dependencies) instead. Shorter tests are faster
to execute and easier to maintain.
**Performance tip:** [Agent Cache](/core-concepts/agent-cache) is enabled by
default, speeding up test execution by reusing AI reasoning from previous
successful runs. Consider disabling cache when debugging flaky tests or
testing new features where you want fresh AI analysis.
### Testing Email Flows
When writing tests that involve emails (like password resets or notifications), follow these best practices to ensure the agent can find the correct email:
* **Specify the Recipient**: Always mention the target email address in your test goal or step (e.g., "Wait for the welcome email sent to `user@example.com`").
* **Use Descriptive Queries**: Describe the email content clearly (e.g., "Find the email with subject 'Reset Password'").
* **Mind the Time Window**: Remember that tests can only see emails received *after* the test started.
* If Test A triggers an email and finishes, Test B cannot see that email if it starts afterwards.
* **Solution**: Combine the trigger and verification into one test, or use dependencies where the second test triggers the email resend.
For more details on how the inbox works, see [Email Inbox](/test-features/email-inbox).
### Test Dependencies
Tests can depend on other tests to control execution order and reuse browser state. This is essential for complex workflows where one test needs data or state from another. Learn more in [Test Dependencies](/core-concepts/dependencies).
To reuse the same sequence of steps across many tests, see [Shared Steps](/core-concepts/shared-steps).
**Testing with Multiple Users:** If your test requires multiple users logged
in simultaneously (e.g., collaboration, sharing), create separate login tests
for each user. Each login test becomes the root of an independent chain with
its own isolated browser session, ensuring users don't interfere with each
other. Learn more about [Multi-User Testing
Scenarios](/core-concepts/dependencies#multi-user-testing-scenarios).
## Organizing Tests for Complex Projects
For projects with multiple products, versions, or environments, see [Projects, Applications, Environments](/core-concepts/applications-and-environments) for organization patterns and best practices.
# Authentication
Source: https://docs.qa.tech/best-practices/handle-auth
To enable authentication in your tests, you'll need to create **Configs** in your project settings. Configs store authentication credentials (like usernames, passwords, and 2FA secrets) that the AI agent uses automatically during test execution. Our AI needs to access your website and act as a registered user to interact with protected features and test user-specific functionality.
If your preferred authentication method isn't listed below, let us know, and we'll consider adding it to our roadmap.
For detailed information about all config types and their settings, see the
[Configs documentation](/core-concepts/configs).
**Security Notice: Use Test Credentials Only** Authentication credentials
stored in [Configs](/core-concepts/configs) are **not encrypted** and are
passed to AI language models during test execution. Always use dedicated test
accounts - never real user credentials or production passwords.
## Authentication Support for QA.tech AI Bots
| Authentication Method | Description | Setup Required |
| ------------------------------- | ---------------------------------------------------------------------------- | ------------------------------------- |
| Username + Password | Standard form-based login with username/email and password | Basic Config |
| Email + Password | Login using email address and password | Basic Config |
| OTP via Email | One-time passwords sent to email addresses | Email Config + Inbox |
| Magic Link Login | Passwordless login via email links | Email Config + Inbox (auto-generated) |
| Two-Factor Authentication (2FA) | Time-based codes from authenticator apps (Google Authenticator, Authy, etc.) | Advanced Config (see below) |
## What We Don't Support
* **BankID** - Requires stubbing in your testing environment or manual authentication via live stream ([see guide](/applications/se-bank-id))
* **CAPTCHA challenges** - Most CAPTCHA types (reCAPTCHA, hCAPTCHA, etc.) are not supported.
**Avoid CAPTCHA on staging/dev:** Whitelist QA.tech's IP addresses to prevent
CAPTCHA challenges during test execution. This allows our AI agents to run
tests without interruption. Find your IP addresses in [**Settings →
Network**](https://app.qa.tech/dashboard/current-project/settings/network) and
see the [IP Access Control guide](/configuration/ip-access-control) for setup
instructions.
***
## Setting Up Authentication
### Basic Credentials (Username/Password or Email/Password)
For standard login forms, create a config in your project settings:
1. Go to **Project Settings** → **Configs**
2. Click **Add Config**
3. Select **Username + Password Credentials** or **Valid Email + Password Login Credentials**
4. Fill in your test account credentials
5. Click **Save**
The AI will automatically use these credentials when it encounters a login form during test execution.
**Build a comprehensive knowledge graph:** If your application has different
user types (admin, regular user, premium user, etc.), create separate
authentication configs for each. Login tests can automatically trigger
[crawling sessions](/core-concepts/crawling) after completion, allowing
QA.tech to map out what each user type can access. This helps the AI generate
more accurate tests tailored to different user permissions.
### Email-Based OTP
For authentication flows that send one-time passwords to email:
1. QA.tech provides dedicated email inboxes for each project
2. The AI can automatically read OTP codes from emails sent to these addresses
3. Configure email settings in **Project Settings** → **Configs**
4. When writing tests, the AI will wait for and extract OTP codes automatically
QA.tech's built-in email inbox system allows the AI to receive and process emails during test execution - no need to set up external email services or worry about email delivery. The AI can wait for emails (up to 3 minutes), extract verification codes, and click links automatically. Learn more about [Email Inbox](/test-features/email-inbox).
### Magic Link Login
For passwordless authentication via email links:
1. QA.tech automatically provides a magic link email address for each project (no setup required)
2. Enter this email address in your application's login form
3. The AI automatically waits for the email, extracts the login link, and navigates to it
Create a test user account in your application using the magic link email
address shown in your project's [**Email for Magic Link Login**
config](/core-concepts/configs) before running tests.
### Two-Factor Authentication (2FA)
QA.tech allows testing login flows that are protected by Two-Factor Authentication (2FA).
#### How 2FA Testing Works
Most modern 2FA systems use **Time-based One-Time Passwords (TOTP)**. This is the constantly changing, 6-digit code you see in apps like **Google Authenticator**, **Authy**, or **Microsoft Authenticator**.
To test this, our AI needs the secret key that your application uses to generate these codes. You provide this secret key to us once, and our AI handles the rest.
* **Your Role (One-Time Setup):** You will create a special `Config` in your QA.tech project settings. Instead of giving us a static 6-digit code, you will provide a special secret key in a format called a **URI**.
* **Our AI's Role (During Every Test):** When the AI agent encounters a 2FA screen during a test, it will use the secret URI you provided to generate a **fresh, valid 6-digit code** at that exact moment. It then automatically enters the code to complete the login.
This way, you never have to worry about codes expiring or manually entering them during a test run.
#### Setting Up a 2FA Config
Follow these steps to create a config for a test user account that has 2FA enabled.
Log in to the application you want to test and go to the security settings for your test user. Find the option to enable Two-Factor Authentication and proceed until the application shows you a **QR code**.
Stop here. Do not scan it with your phone. We need to extract the secret key from this image.
The QR code contains a secret key that our AI needs. You can use a free online tool to extract this key.
1. Take a screenshot of the QR code.
2. Go to a site like [**scanqr.org**](https://scanqr.org/) in your browser.
3. Upload the screenshot of the QR code.
4. The tool will decode it and reveal a string of text called a URI. Copy this entire string. It will look something like this:
```text theme={null}
otpauth://totp/YourApp:test.user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=YourApp
```
Now, let's add the credentials and the secret URI to QA.tech.
1. Go to **Project Settings** → **Configs**
2. Click the **Add Config** button.
3. From the list of types, select **Username + Password Credentials - with Two-Factor Authentication**.
4. Fill in the form:
* **Config Name:** Give it a memorable name (e.g., "Admin User with 2FA").
* **Username:** The username for your test account.
* **Password:** The password for your test account.
* **One-Time Password URI:** Paste the full `otpauth://` URI you copied from the QR code scanner.
5. Click **Save**.
#### Running Tests with 2FA Enabled
You're all set! You do not need to add any special instructions to your tests like "enter the 2FA code."
When you ask the AI to perform a test that involves logging in (e.g., "Log in and check the user dashboard"), the agent will automatically:
1. Use the username and password from your new config.
2. When it sees the 2FA input screen, it will automatically generate a valid code using the URI you provided.
3. It will enter the code and complete the login before proceeding with the rest of your test instructions.
# Running Tests
Source: https://docs.qa.tech/best-practices/running-tests
Tests in QA.tech can be run in several ways.
## Trigger a Run from the UI
The simplest way to get started is to use the "Run Tests" button found on the [Dashboard](https://app.qa.tech/current-project) or the [Test Cases](https://app.qa.tech/current-project/scenarios) page.
You can also run individual tests by navigating to the test and clicking **Run Test**.
### Run with Dependencies
When running an individual test that has dependencies, you have two options:
* **Run Test** - Uses [dependency output state optimization](/core-concepts/output-state-optimization) to skip recently completed dependencies (completed within 6 hours)
* **Run w. Dependencies** - Forces all dependency tests to run fresh, generating new output data
Use **Run w. Dependencies** when you need fresh time-sensitive data like OTP codes or temporary tokens that may have expired. [Learn more](/core-concepts/output-state-optimization#my-test-is-using-stale-output-data-old-otp-codes-expired-tokens).
When running multiple tests via a **Test Plan**, QA.tech automatically
executes them in parallel where the dependency graph allows, with auto-scaling
up to approximately 100 concurrent agents when no limit is configured. You can
set **Maximum Concurrent Tests** on each environment to throttle parallel runs
against sensitive URLs. Learn more in [Parallel Test Execution and Concurrency
Limits](/core-concepts/parallel-test-execution) and [Test
Plans](/core-concepts/test-plans#performance--parallel-execution).
## Trigger a Run as Part of a Pipeline
You can also integrate test runs as part of your **CI pipeline**.
You can use any system that supports sending an HTTPS POST request when you want to trigger the tests.
For a great example, check out our [GitHub Actions documentation](/configuration/github-actions).
## Trigger a Run on a Schedule
If you want your tests to run at a specific time, such as on a recurring weekly schedule (cron-style), we offer that functionality as well.
Go to [Test Plans](https://app.qa.tech/current-project/test-plans) in your
project dashboard.
Click on an existing test plan that you want to schedule, or create a new
one if needed.
Click on **Manage Schedules** within the test plan.
Add your cron schedule expression and a description, then click **Add
Schedule**.
If you're unsure how to write the cron format, you can use a tool like [crontab.guru](https://crontab.guru/) or reach out to us, and we'll be happy to help you get set up.
# How to review test results
Source: https://docs.qa.tech/best-practices/test-results
Different stages of your development workflow need different testing approaches. This guide helps you choose the right testing strategy for your needs and efficiently manage results across your workflow.
**Foundation for Testing**
Before using any testing approach below, ensure you've [crawled your website](/core-concepts/crawling) to build the [knowledge graph](/core-concepts/knowledge-graph) and configured [authentication](/best-practices/handle-auth). These prerequisites enable the AI agent to test efficiently.
## Choosing Your Testing Strategy
Different testing approaches fit different scenarios. Choose based on when you need results and what you're validating:
| Testing Approach | Best For | When to Use | Setup Guide |
| --------------------- | ------------------------------------------ | ------------------------------------------ | --------------------------------------------------- |
| Manual test runs | Ad-hoc testing, debugging, development | Quick validation, iterating on test design | [Running Tests](/best-practices/running-tests) |
| Scheduled test plans | Production monitoring, regression suites | Daily/nightly checks, SLA monitoring | [Test Plans](/core-concepts/test-plans) |
| PR-triggered testing | Pre-merge validation, preview environments | Every PR, feature branches | [GitHub Actions](/configuration/github-actions) |
| API-triggered testing | Custom workflows, deployment pipelines | Post-deploy validation, multi-environment | [API Reference](/api-reference/runs/start-test-run) |
**Recommended progression:**
* **Start with:** Manual runs while building tests, then add PR testing
* **Add next:** Scheduled nightly runs for comprehensive coverage
* **Advanced:** API integration for custom deployment workflows
* **Pro tip:** Use test plans to organize tests for different scenarios (smoke vs. full regression)
## Organizing Tests with Test Plans
Test plans help you run the right tests at the right time. Organize tests strategically to separate fast smoke tests from comprehensive regression suites.
**Common test plan patterns:**
* **Smoke tests plan** - 5-10 critical tests, runs on every PR (\~5 min)
* **Regression plan** - Full suite, runs nightly (\~30-60 min)
* **Production monitoring plan** - Key flows, runs every 4 hours
* **Preview environment plan** - Dynamic URLs, runs on deploy
Test plans enable parallel execution - 20 tests complete in \~3 minutes instead
of 60 minutes sequential. See [Test
Plans](/core-concepts/test-plans#performance--parallel-execution).
Use test plans to:
* Separate fast tests (PR-triggered) from slow tests (scheduled)
* Target different environments (staging vs. production)
* Control what runs when based on your workflow
### Quick Triage Workflow
Use RESULT=FAILED + STATUS=COMPLETED to see only actionable failures. See
[Tests and
Results](/core-concepts/tests-and-results#filtering-and-searching-results)
for filter options.
Grouped failures often share root cause - review scenario by scenario
Determine if it's a product bug, test needs update, flaky test, or
environment issue
Create issues for bugs, update tests for intentional changes, add waits for
flaky tests
## Getting Notified
Configure notifications based on your testing approach:
* **PR tests** - GitHub comments post automatically (see [GitHub Actions](/configuration/github-actions))
* **Scheduled runs** - Slack/email for failures (see [Notifications](/core-concepts/notifications))
* **Production monitoring** - Immediate alerts for critical failures
**Tip:** Set up different notification channels for different severity levels (Slack for critical, email for nightly)
## Exporting Issues
When you find bugs during review:
* Export to Linear, Jira, or Trello: [Configure integrations](/integrations/linear)
* Include playback URL and context before exporting
* Tag with environment and test run for traceability
# Troubleshooting
Source: https://docs.qa.tech/best-practices/troubleshooting
What can you do to make QA.tech work as well as possible for you?
## The agent doesn't understand what to click
We use a combination of the DOM and visual models to decide what to click or interact with. To make it easier for our agent to understand your site, we suggest taking the following measures.
1. Make your site more accessible
Ensure all your elements are tagged with the correct `aria`-labels, as well as `title` and `alt` attributes.
2. Use semantic HTML tags
For example, utilize `` or `` element for clickable elements (as opposed to `div`s).
# QA.tech Bot
Source: https://docs.qa.tech/bot
Identify and verify QA.tech automated testing traffic.
**QATechBot** is the automated browser identity used by [QA.tech](https://qa.tech) when customers run AI-powered functional and regression tests against their own web applications. Traffic is **only sent to sites and environments the customer has explicitly configured** in QA.tech—not for crawling, indexing, or scraping the public web.
***
## What QATechBot does
| Aspect | Detail |
| ------------ | ------------------------------------------------------------------------------------------------------------------------- |
| **Purpose** | Execute authorized end-to-end tests (clicks, navigation, forms) in a real browser, driven by customer-defined test cases. |
| **Category** | Monitoring & testing automation (not a search engine or content harvester). |
| **Scope** | URLs under applications and environments the customer adds in QA.tech. |
***
## User-Agent
Requests from QA.tech browser automation append an identifiable suffix so you can recognize the bot in logs and WAF rules.
**Suffix (always present on bot traffic):**
```http theme={null}
QATechBot/1.0 (+https://docs.qa.tech/bot)
```
The full `User-Agent` is typically a standard Chromium-based string **plus** this suffix (device presets may vary the base browser UA).
Standalone fetches that identify as the bot (e.g. when reading `robots.txt`) use:
```http theme={null}
User-Agent: QATechBot/1.0 (+https://docs.qa.tech/bot)
```
***
## Verifying QATechBot traffic
If you use **Cloudflare** at the edge (WAF / firewall) or embed **Turnstile** in your app, see **[Cloudflare WAF & Turnstile](/configuration/cloudflare-waf-turnstile)**—edge rules are configured in the dashboard; Turnstile is configured in your application.
If you use the **Vercel Firewall**, see **[Vercel Firewall](/configuration/vercel-firewall)** to allow QATechBot traffic with a bypass rule.
QA.tech supports verification in two complementary ways:
### 1. IP allowlist and reverse DNS
* **Published IPv4 prefixes:**\
`https://app.qa.tech/.well-known/qatech-ips.json`\
(JSON with `ipv4Prefix` entries, updated as infrastructure changes.)
* **Reverse DNS:** Egress IPs resolve (PTR) to **`bot.qa.tech`**, and forward DNS for `bot.qa.tech` matches those same static IPs—so IP ownership can be validated end-to-end.
### 2. Web Bot Auth (signed requests)
For **top-level page navigations**, QA.tech may attach **HTTP Message Signatures** ([Web Bot Auth](https://github.com/cloudflare/web-bot-auth)) so verifiers can cryptographically confirm the request came from QA.tech.
* **Public key directory:**\
`https://app.qa.tech/.well-known/http-message-signatures-directory`
Sub-resource requests (images, scripts, stylesheets) are usually **not** signed; IP validation still applies to those.
***
## robots.txt
Where QA.tech performs discovery or policy checks against `robots.txt`, requests use the `QATechBot/1.0` user agent and respect normal robots rules for the customer’s configured URLs.
***
## Privacy and authorization
* Tests run **on behalf of the QA.tech customer** who configured the target application.
* QA.tech does **not** use QATechBot to index or bulk-scrape arbitrary sites.
* If you believe traffic is unauthorized, contact your counterpart at the organization using QA.tech, or reach out to QA.tech support.
# qatech applications
Source: https://docs.qa.tech/cli/commands/applications
List the applications in your QA.tech project.
Lists every application in the project with its short ID and type (web or mobile). Use the short ID with [`qatech environments`](/cli/commands/environments) or with `--application-overrides` on [`qatech run`](/cli/commands/run) and [`qatech chat`](/cli/commands/chat).
## Usage
```bash theme={null}
qatech applications [options]
```
## Options
| Flag | Short | Description |
| ----------------- | ----- | ---------------------------- |
| `--json` | `-j` | Machine-readable output |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
## Examples
```bash theme={null}
qatech applications
qatech applications --json
```
## Sample output
```text theme={null}
Applications:
My Web App (app-myapp_Abc123) - web
My Mobile App (app-mymobile_Def456) - mobile
```
# qatech chat
Source: https://docs.qa.tech/cli/commands/chat
Talk to the QA.tech agent - get test IDs, ask about coverage, and run tests against custom URLs.
Sends a message to the QA.tech agent. The agent knows your test cases, applications, and configurations, and returns test case IDs you can pipe directly into [`qatech run`](/cli/commands/run). Useful for both scripting (single-shot) and exploration (interactive REPL).
## Usage
```bash theme={null}
# Single-shot
qatech chat [options] ""
# Interactive REPL
qatech chat [options]
# From stdin
echo "" | qatech chat
qatech chat --stdin <<'EOF'
EOF
```
## Options
| Flag | Short | Description |
| -------------------------------- | ----- | ------------------------------------------------------------- |
| `--conversation ` | `-c` | Continue an existing conversation (chat short ID) |
| `--application-overrides ` | | Override application target URLs as a JSON array |
| `--json` | `-j` | Machine-readable output (single-shot only) |
| `--stdin` | | Force reading the message from stdin even when stdin is a TTY |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
## Modes
* **Single-shot** - pass a message argument or pipe stdin. Streams the agent's response, then exits.
* **Interactive** - invoked with no message on a TTY. Drops you into a REPL; type `exit` or `quit` to leave.
* **Resume** - pass `-c ` to continue a previous conversation in either mode.
## Examples
```bash theme={null}
# Ask a question
qatech chat "What tests cover the checkout flow?"
# Pipe IDs into a run
ID=$(qatech chat --json "Most critical login test, just the ID" | jq -r '.content')
qatech run -c "$ID" --wait
# Continue a conversation
qatech chat -c chat_abc123 "Now run those tests"
# Interactive REPL
qatech chat
```
## Testing against a custom URL
When you want the agent to test against a URL that isn't a configured environment - e.g. a tunnel from [`qatech tunnel`](/cli/commands/tunnel) or a PR preview - pass `--application-overrides`:
```bash theme={null}
qatech chat --application-overrides '[{
"applicationShortId": "app-myapp_Abc123",
"environment": {"url": "https://preview.example.com"}
}]' "Test the login flow"
```
For multiple apps:
```bash theme={null}
qatech chat --application-overrides '[
{"applicationShortId": "app-myapp_Abc123", "environment": {"url": "https://web.preview.example.com"}},
{"applicationShortId": "app-myapi_Def456", "environment": {"url": "https://api.preview.example.com"}}
]' "Run the full test suite"
```
The `environment` object accepts any of:
| Form | When to use |
| ---------------------------------------- | ------------------------------------------- |
| `{"url": "..."}` | Inline URL override (tunnel, preview, etc.) |
| `{"shortId": "env_xxx"}` | Reuse a saved environment |
| `{"applicationBuildShortId": "bld_xxx"}` | Pin to a specific build |
Application overrides apply to the **first** message of a conversation. When
resuming with `-c`, the existing conversation already has its environment.
## JSON output
```json theme={null}
{
"conversationShortId": "chat_abc123",
"messageId": "e5f6g7h8-...",
"content": "Here are the tests covering checkout: ...",
"url": "https://app.qa.tech/..."
}
```
# qatech configure
Source: https://docs.qa.tech/cli/commands/configure
Set up and inspect your QA.tech CLI credentials.
Configures the CLI with your QA.tech API key. By default the key is saved to `.qatech/config.json` in the current directory so each project can have its own key. Use `--global` to write to `~/.qatech/config.json` as a fallback.
## Usage
```bash theme={null}
qatech configure [options]
```
## Options
| Flag | Short | Description |
| ----------------- | ----- | ------------------------------------------------------------- |
| `--api-key ` | `-k` | Set the API key (saves to local config by default) |
| `--global` | `-g` | Save to `~/.qatech/config.json` instead of project-local |
| `--show` | `-s` | Show the active configuration and where each value comes from |
| `--help` | `-h` | Show command help |
## Examples
```bash theme={null}
# Interactive setup (saves locally)
qatech configure
# Non-interactive - set the project-local key
qatech configure -k
# Set the global fallback key
qatech configure -k --global
# Inspect what the CLI is using
qatech configure --show
```
## Resolution order
The resolved key is the first match from:
1. `--api-key` flag on the running command
2. `QATECH_API_KEY` environment variable
3. `.qatech/config.json` in the current directory (project-local)
4. `~/.qatech/config.json` (global fallback)
`qatech configure --show` reports the active key (masked), the source it came from, and both config file paths so you can see exactly which file you're editing.
## Security
The config file stores your raw API key - treat it like any other credential. Add `.qatech/` to your `.gitignore` if you want to keep project-local keys out of source control.
Get your API key from [app.qa.tech](https://app.qa.tech) → **Settings →
Integrations → API**.
# qatech environments
Source: https://docs.qa.tech/cli/commands/environments
List the environments configured for an application.
Lists the non-preview environments for a given application along with each environment's URL and short ID.
## Usage
```bash theme={null}
qatech environments [options]
```
## Arguments
| Argument | Description |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| `` | The application short ID (e.g. `app-myapp_Abc123`). Find it with [`qatech applications`](/cli/commands/applications). |
## Options
| Flag | Short | Description |
| ----------------- | ----- | ---------------------------- |
| `--json` | `-j` | Machine-readable output |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
## Examples
```bash theme={null}
qatech environments app-myapp_Abc123
qatech environments app-myapp_Abc123 --json
```
## Sample output
```text theme={null}
Environments for app-myapp_Abc123:
Production: https://app.example.com (env_abc123) [production]
Staging: https://staging.example.com (env_def456)
```
# qatech init
Source: https://docs.qa.tech/cli/commands/init
Generate Claude Code subagent and skill files for the QA.tech CLI.
`qatech init` writes two files into the current project that teach Claude Code how to use this CLI:
```
.claude/agents/qa-runner.md # subagent definition
.claude/skills/qa-tech-cli/SKILL.md # full CLI reference for the agent
```
These files contain no credentials and are safe to commit to your repo. Re-run with `--force` after upgrading the CLI to pull in the latest skill and agent content.
## Usage
```bash theme={null}
qatech init [options]
```
## Options
| Flag | Short | Description |
| -------------- | ----- | ----------------------------------------------------- |
| `--dir ` | `-d` | Target project directory (default: current directory) |
| `--force` | `-f` | Overwrite existing files (useful after CLI upgrades) |
| `--help` | `-h` | Show command help |
## Examples
```bash theme={null}
# Create files in the current project
qatech init
# Initialize a different project
qatech init --dir /path/to/project
# Refresh after upgrading the CLI
qatech init --force
```
## What you get
After running `qatech init`, Claude Code (or any agent that reads `.claude/`) can:
* Invoke the `qa-runner` subagent via the **Task** tool to run E2E tests in the background.
* Read the `qa-tech-cli` skill - a complete CLI reference with examples, ID formats, JSON shapes, and error handling - whenever it needs to use `qatech`.
## Updating after CLI upgrades
The skill and agent files are baked into the CLI itself, so they evolve as the CLI evolves. After `npm install -g qatech@latest`, run:
```bash theme={null}
qatech init --force
```
…in each project that has the integration installed, then commit the diff.
# qatech run
Source: https://docs.qa.tech/cli/commands/run
Start a test run by test plan or individual test cases.
Starts a QA.tech test run. Pass either a test plan or one or more individual test cases. With `--wait`, the command polls until completion and exits non-zero on failure.
## Usage
```bash theme={null}
qatech run (-t | -c ...) [options]
```
You must provide either `--test-plan` **or** one or more `--test-case` flags - not both.
## Options
| Flag | Short | Description |
| -------------------------------- | ----- | ---------------------------------------------------------------------------------------- |
| `--test-plan ` | `-t` | Test plan short ID (e.g. `pln_abc123`) |
| `--test-case ` | `-c` | Test case UUID (repeatable) |
| `--application-overrides ` | | JSON array of application overrides - point apps at custom URLs, environments, or builds |
| `--wait` | `-w` | Poll until the run finishes, then print results |
| `--poll-interval ` | | How often to check status when waiting (default: `5`) |
| `--timeout ` | | Max time to wait before giving up (default: `600`) |
| `--json` | `-j` | Machine-readable output. Progress goes to stderr. |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
## Behavior
* **Without `--wait`** - prints the run short ID and exits immediately.
* **With `--wait`** - polls until `COMPLETED`, `ERROR`, or `CANCELLED`, then prints results.
* Exit code is `1` if any test case ends as `FAILED`, `0` otherwise.
* With `--wait --json`, progress logs go to stderr so stdout stays clean JSON.
## Examples
```bash theme={null}
# Run a full test plan and wait for results
qatech run -t pln_abc123 --wait
# Run specific test cases
qatech run -c 636a990b-85e7-44c2-8175-58390f2184a3 --wait
# Run multiple test cases with JSON output
qatech run -c -c -w --json
# Run against a preview deployment
qatech run -t pln_abc123 \
--application-overrides '[{"applicationShortId":"app-myapp_Abc123","environment":{"url":"https://preview.example.com"}}]' \
--wait
# Fire-and-forget - check later with `qatech status`
qatech run -t pln_abc123
```
## Application overrides
`--application-overrides` redirects one or more applications to a different URL, saved environment, or build for this run. Same JSON shape as [`qatech chat --application-overrides`](/cli/commands/chat) - useful for testing preview deployments, tunnels from [`qatech tunnel`](/cli/commands/tunnel), or any URL not configured as a saved environment.
```bash theme={null}
qatech run -t pln_abc123 \
--application-overrides '[
{"applicationShortId": "app-myapp_Abc123", "environment": {"url": "https://web.preview.example.com"}},
{"applicationShortId": "app-myapi_Def456", "environment": {"url": "https://api.preview.example.com"}}
]' \
--wait --json
```
The `environment` object accepts any of:
| Form | When to use |
| ---------------------------------------- | ------------------------------------------- |
| `{"url": "..."}` | Inline URL override (tunnel, preview, etc.) |
| `{"shortId": "env_xxx"}` | Reuse a saved environment |
| `{"applicationBuildShortId": "bld_xxx"}` | Pin to a specific build |
## JSON output
The shape returned by `qatech run --wait --json` matches [`qatech status --json`](/cli/commands/status):
```json theme={null}
{
"shortId": "UkxK",
"status": "COMPLETED",
"result": "PASSED",
"runTestCases": [
{
"id": "636a990b-...",
"shortId": "AbCd",
"name": "Login with valid credentials",
"status": "COMPLETED",
"result": "PASSED",
"resultTitle": null,
"evaluationThought": "The test passed because..."
}
]
}
```
Key fields:
* `result` - `"PASSED" | "FAILED" | "SKIPPED" | null`
* `runTestCases[].resultTitle` - human-readable failure reason (`null` if passed)
* `runTestCases[].evaluationThought` - agent's reasoning about the result
# qatech status
Source: https://docs.qa.tech/cli/commands/status
Check or wait for the result of a test run.
Returns the current status of a run, or polls until it finishes. Use this when you started a run with `qatech run` (without `--wait`) and want to check on it later.
## Usage
```bash theme={null}
qatech status [options]
```
## Arguments
| Argument | Description |
| ---------- | ----------------------------------------------------------------------------------------------------------- |
| `` | Run short ID (e.g. `UkxK`). Returned by [`qatech run`](/cli/commands/run) and visible in the dashboard URL. |
## Options
| Flag | Short | Description |
| ------------------------ | ----- | ----------------------------------------------------- |
| `--wait` | `-w` | Poll until the run completes, then print results |
| `--poll-interval ` | | How often to check status when waiting (default: `5`) |
| `--timeout ` | | Max time to wait before giving up (default: `600`) |
| `--json` | `-j` | Machine-readable output |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
## Run statuses
| Status | Meaning |
| ----------- | --------------------------------------------------------- |
| `INITIATED` | Queued, not yet executing |
| `RUNNING` | Tests are running |
| `COMPLETED` | All tests finished - check `result` for `PASSED`/`FAILED` |
| `ERROR` | Run failed due to an infrastructure error |
| `CANCELLED` | Run was cancelled before completion |
Exit code is `1` if `result` is `FAILED`, `0` otherwise.
## Examples
```bash theme={null}
# One-shot status check
qatech status UkxK
# Block until finished
qatech status UkxK --wait
# Wait, then get JSON
qatech status UkxK --wait --json
# Just the top-line result
qatech status UkxK -j | jq '.result'
```
## JSON output
```json theme={null}
{
"shortId": "UkxK",
"status": "COMPLETED",
"result": "FAILED",
"runTestCases": [
{
"id": "636a990b-...",
"shortId": "AbCd",
"name": "Checkout with saved card",
"status": "COMPLETED",
"result": "FAILED",
"resultTitle": "Card form did not submit",
"evaluationThought": "..."
}
]
}
```
Without `--wait`, the response only includes failed test cases (`testCases=failed` server-side). With `--wait`, you get the full set.
# qatech test-cases
Source: https://docs.qa.tech/cli/commands/test-cases
List and search test cases in your project.
Lists the test cases in your project. Each test case has a UUID that you can pass to `qatech run -c ` to execute it.
## Usage
```bash theme={null}
qatech test-cases [options]
```
## Options
| Flag | Short | Description |
| ------------------------- | ----- | --------------------------------------------------------------- |
| `--application ` | `-a` | Filter by application short ID (e.g. `app-myapp_Abc123`) |
| `--labels ` | `-l` | Filter by labels (comma-separated, e.g. `smoke,critical`) |
| `--search ` | `-s` | Filter by name or goal text (case-insensitive, applied locally) |
| `--enabled ` | | Filter by enabled/disabled status |
| `--include-steps` | | Include the step-by-step definition of each test case |
| `--limit ` | | Max results per page (default: 100, max: 1000) |
| `--offset ` | | Pagination offset (default: 0) |
| `--json` | `-j` | Machine-readable output |
| `--api-key ` | | Per-command API key override |
| `--help` | `-h` | Show command help |
`--application`, `--labels`, and `--enabled` are server-side filters. `--search` is applied locally to the returned page, so combine it with the server-side filters when you have many test cases.
## Examples
```bash theme={null}
# List the first 100 test cases
qatech test-cases
# Find login-related tests
qatech test-cases --search "login"
# Combine filters
qatech test-cases --enabled true -a app-myapp_Abc123 -l smoke,critical
# Pagination
qatech test-cases --offset 100 --limit 100
# Extract every test case ID
qatech test-cases --json | jq '.testCases[].id'
# Export all test case definitions including steps
qatech test-cases --include-steps --json > test-cases.json
```
## JSON output
```json theme={null}
{
"testCases": [
{
"id": "636a990b-85e7-44c2-8175-58390f2184a3",
"name": "Login with valid credentials",
"isEnabled": true,
"labels": ["smoke", "auth"],
"applicationShortId": "app-myapp_Abc123",
"applicationName": "My App",
"goal": "Verify user can log in",
"expectedResult": "User lands on the dashboard",
"lastRun": {
"shortId": "UkxK",
"status": "COMPLETED",
"result": "PASSED",
"completedAt": "2026-02-26T12:00:00Z"
}
}
],
"total": 42,
"limit": 100,
"offset": 0
}
```
With `--include-steps`, each test case also contains a `steps` array with the step-by-step definition (`instruction`, plus optional `shortName` and `expectedResult` per step). See [Exporting Test Cases](/api-reference/exporting-test-cases) for how to turn this into a CSV for other test management tools.
Use the `id` field with [`qatech run -c `](/cli/commands/run).
# qatech tunnel
Source: https://docs.qa.tech/cli/commands/tunnel
Expose local ports to QA.tech and run tests against your dev server.
Exposes local ports to QA.tech so the agent can reach your dev server during testing. Pair with `--application-overrides` on [`qatech run`](/cli/commands/run) or [`qatech chat`](/cli/commands/chat) to point a test run at the tunnel URL.
## Usage
```bash theme={null}
qatech tunnel start [options] # start a tunnel
qatech tunnel list # list active tunnels for your project
qatech tunnel status # check tunnel health
qatech tunnel stop # tear down a tunnel
```
## Global flags
These work on every subcommand:
| Flag | Short | Description |
| ----------------- | ----- | -------------------------------------- |
| `--api-key ` | `-k` | API key override (or `QATECH_API_KEY`) |
| `--api-url ` | | API base URL (or `QATECH_API_URL`) |
| `--help` | `-h` | Show subcommand help |
## `qatech tunnel start`
Starts a tunnel and prints a manifest of public URLs, one per port.
| Flag | Short | Description |
| ---------------- | ----- | ---------------------------------------------------------------- |
| `--port ` | `-p` | Ports to expose, comma-separated, with optional labels |
| `--https` | | Local services use HTTPS (default: HTTP) |
| `--public` | | Make the tunnel publicly accessible (no authentication required) |
### Port syntax
| Form | Example | Result |
| ----------- | -------------------------- | ------------------------ |
| Single port | `--port 3000` | One tunnel for port 3000 |
| Multi-port | `--port 3000,8080` | One tunnel host per port |
| Labelled | `--port 3000:web,4000:api` | Hostnames keyed by label |
A labelled multi-port tunnel returns a manifest with one URL per label:
```text theme={null}
web -> https://web-.quack.run
api -> https://api-.quack.run
```
### Examples
```bash theme={null}
# Single port
qatech tunnel start --port 3000
# Multiple ports with labels
qatech tunnel start --port 3000:web,4000:api
# Public tunnel (no auth wrapper)
qatech tunnel start --port 3000 --public
# Local server uses HTTPS
qatech tunnel start --port 8443 --https
# Use the tunnel URL in a run
qatech tunnel start --port 3000
qatech run -t pln_abc123 \
--application-overrides '[{"applicationShortId":"app-myapp_Abc123","environment":{"url":"https://.quack.run"}}]' \
--wait
```
## `qatech tunnel list`
```bash theme={null}
qatech tunnel list
```
Lists every active tunnel for the project - runner ID, port mapping, and public URLs.
## `qatech tunnel status`
```bash theme={null}
qatech tunnel status
```
Returns the live health status of a single tunnel.
## `qatech tunnel stop`
```bash theme={null}
qatech tunnel stop
```
Tears down a tunnel and cleans up its resources. If the tunnel process is still running in another terminal, it will exit on its own within \~30 seconds.
# CLI Overview
Source: https://docs.qa.tech/cli/overview
Run QA.tech tests, inspect results, expose local servers, and chat with the QA.tech agent from the terminal.
# qatech CLI
`qatech` is the official command-line tool for [QA.tech](https://qa.tech). It lets you run end-to-end tests, inspect results, expose local servers via tunnels, and chat with the QA.tech agent - all from the terminal.
The CLI is designed to be **agent-friendly**: every command supports `--json` output, `--help` with copy-pasteable examples, and exits non-zero on failure so AI coding agents can drive it reliably from your terminal.
## Install
```bash theme={null}
npm install -g qatech
```
Requires Node.js 18+.
## Quick start
Sign in at [app.qa.tech](https://app.qa.tech) → **Settings → Integrations → API**.
```bash theme={null}
qatech configure -k
```
The key is saved to `.qatech/config.json` in the current directory by default.
```bash theme={null}
qatech test-cases
```
```bash theme={null}
qatech run -c --wait
```
Streams progress to your terminal and exits with code `1` if any test fails.
## Commands
| Command | Description |
| -------------------------------------------- | --------------------------------------------- |
| [`configure`](/cli/commands/configure) | Set up API credentials |
| [`test-cases`](/cli/commands/test-cases) | List and search test cases |
| [`applications`](/cli/commands/applications) | List applications in the project |
| [`environments`](/cli/commands/environments) | List environments for an application |
| [`run`](/cli/commands/run) | Start a test run |
| [`status`](/cli/commands/status) | Check or wait for a run's results |
| [`chat`](/cli/commands/chat) | Chat with the QA.tech agent |
| [`tunnel`](/cli/commands/tunnel) | Expose local ports via tunnels |
| [`init`](/cli/commands/init) | Generate Claude Code subagent and skill files |
Run `qatech --help` for detailed flags on any command.
## Configuration resolution
The CLI resolves credentials in this order - first match wins:
1. `--api-key` flag (per-command override)
2. `QATECH_API_KEY` environment variable
3. `.qatech/config.json` in the current directory (project-local)
4. `~/.qatech/config.json` (global fallback)
| Environment variable | Description |
| -------------------- | --------------------------------------------- |
| `QATECH_API_KEY` | API key |
| `QATECH_API_URL` | API base URL (default: `https://api.qa.tech`) |
| `QATECH_DEBUG` | Set to `1` for stack traces on errors |
## Output conventions
* **stdout** → data (JSON or human-readable results)
* **stderr** → progress messages, errors, hints
* With `--json`, stdout is always valid JSON - safe to pipe to `jq`
* Errors with `--json` are also JSON: `{ "error": true, "message": "...", "statusCode": 401 }`
This split lets you pipe results into `jq` cleanly:
```bash theme={null}
qatech run -t pln_abc123 --wait --json | jq '.runTestCases[] | select(.result == "FAILED")'
```
## See also
* [API reference](/api-reference/introduction)
# Authentication Rate Limits
Source: https://docs.qa.tech/configuration/auth-rate-limits
Configure authentication rate limits to prevent issues during parallel testing
When running multiple tests in parallel, authentication rate limits from various providers can be triggered, causing user sessions to be logged out in the middle of tests. This guide covers how to configure rate limits for different authentication providers.
## Overview
Authentication providers implement rate limiting to prevent abuse and ensure service stability. During testing, these limits can impact:
* Parallel test execution
* Authentication flows in tests
* Session management during long-running tests
* User signup/verification processes
## Supabase Rate Limits
Supabase provides comprehensive rate limiting controls that are particularly important when running parallel tests against free or limited tier instances.
### Accessing Supabase Rate Limits
1. Navigate to your Supabase project dashboard
2. Go to **Authentication** → **Rate Limits** in the sidebar
3. Configure the various rate limit settings
### Rate Limit Settings
#### Email Rate Limits
* **Purpose**: Controls how many emails can be sent per hour
* **Default**: 500 emails per hour
* **Testing Impact**: Affects signup/verification flows in tests
* **Recommendation**: Increase if tests involve many user registrations
#### Token Refresh Rate Limits
* **Purpose**: Controls session refreshes in 5-minute intervals per IP
* **Default**: 300 refreshes per 5 minutes (3600 requests per hour)
* **Testing Impact**: **High impact** - Can cause session logouts during long-running tests
* **Recommendation**: Increase significantly for parallel testing (e.g., 1000-5000)
### Supabase-Specific Notes
* Rate limit changes take effect immediately
* Some limits are per IP address, others are per project
* Free tier projects have lower default limits than paid plans
* Consider upgrading to a paid plan for higher default limits
Be cautious when significantly increasing rate limits in production
environments. Higher limits reduce protection against actual abuse and DDoS
attacks.
# Authentication Settings
Source: https://docs.qa.tech/configuration/authentication
Configure authentication for your QA.tech account
## User Account Multi-Factor Authentication (MFA)
Enable Multi-Factor Authentication to verify your identity for an extra layer of security to your QA.tech account in case your password is compromised. In addition to entering your password, MFA requires you confirm your identity via an authenticator app.
You can enable MFA under **Project Settings** → **Profile** → **Authentication**.
This MFA setting secures your QA.tech account. For testing applications that
require 2FA login, see [Two-Factor Authentication
(2FA)](/best-practices/handle-auth#two-factor-authentication-2fa) in the best
practices guide.
# Bitrise
Source: https://docs.qa.tech/configuration/bitrise
Trigger QA.tech test runs from Bitrise CI/CD: upload your Android or iOS build and run mobile test plans automatically
Bitrise is a mobile-focused CI/CD platform. The most common QA.tech integration is to upload the app produced by your build step and trigger a test plan against that exact build. Web applications work too, using a plain HTTP trigger.
## Prerequisites
You need three values before configuring your workflow:
* **API Token** - Your QA.tech API token (from **Settings → Organization → API Keys**)
* **Application Short ID** - Your mobile application's short ID (from **Settings → Applications & Envs**, e.g. `app_gXeBl2`)
* **Test Plan Short ID** - From your test plan (e.g. `pln_abc123`)
New to mobile app testing on QA.tech? Start with [Mobile App
Testing](/test-features/mobile-app-testing) to create your mobile application
first.
### Configure a Bitrise Secret
Store your API token securely:
1. Open your app in Bitrise and go to **Workflow Editor → Secrets**
2. Add a secret:
* **Key**: `QATECH_API_TOKEN`
* **Value**: Your API token
* Keep **Expose for Pull Requests** disabled unless you need it
## Test your mobile builds
Add a `script` step **after** your build step (e.g. `android-build` or `xcode-build-for-simulator`). It uploads the build to QA.tech and starts a test run against it, in four parts:
1. Get a presigned upload URL
2. Upload the build file directly to storage
3. Create the build record
4. Start a test run pinned to that build
### Android (APK)
Bitrise's `android-build` step exposes the built APK as `$BITRISE_APK_PATH`:
```yaml theme={null}
- script@1:
title: Run QA.tech tests on this build
inputs:
- content: |
#!/usr/bin/env bash
set -euo pipefail
APP_ID="app_gXeBl2" # Your QA.tech application short ID
TEST_PLAN_ID="pln_abc123" # Your test plan short ID
BUILD_FILE="$BITRISE_APK_PATH" # Set by the android-build step
FILE_NAME=$(basename "$BUILD_FILE")
# 1. Get a presigned upload URL
UPLOAD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds/upload-url" \
-H "Authorization: Bearer $QATECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"fileName\": \"$FILE_NAME\"}")
UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.uploadUrl')
BUILD_TOKEN=$(echo "$UPLOAD_RESPONSE" | jq -r '.buildToken')
# 2. Upload the file directly to storage
curl -sSf -X PUT "$UPLOAD_URL" \
--upload-file "$BUILD_FILE" \
-H "Content-Type: application/octet-stream"
# 3. Create the build record
BUILD_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/applications/$APP_ID/builds" \
-H "Authorization: Bearer $QATECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"platform\": \"android\", \"buildToken\": \"$BUILD_TOKEN\"}")
BUILD_SHORT_ID=$(echo "$BUILD_RESPONSE" | jq -r '.applicationBuildShortId')
echo "Build created: $BUILD_SHORT_ID"
# 4. Start a test run against this build
RUN_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/run" \
-H "Authorization: Bearer $QATECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"testPlanShortId\": \"$TEST_PLAN_ID\",
\"applications\": [{
\"applicationShortId\": \"$APP_ID\",
\"environment\": {
\"applicationBuildShortId\": \"$BUILD_SHORT_ID\"
}
}]
}")
echo "Test run started: $(echo "$RUN_RESPONSE" | jq -r '.run.url')"
```
Replace `app_gXeBl2` and `pln_abc123` with your values.
### iOS (Simulator build)
QA.tech runs iOS tests on simulators, so the upload must be a **simulator build** (`.app` compressed as `.zip` or `.tar.gz`) - device and App Store `.ipa` builds cannot run on simulators. See [Mobile App Testing](/test-features/mobile-app-testing) for how to prepare a simulator build.
On Bitrise, use the `xcode-build-for-simulator` step instead of `xcode-archive`. It exposes the built `.app` directory as `$BITRISE_APP_DIR_PATH`. Zip it before the upload in the script above:
```bash theme={null}
cd "$(dirname "$BITRISE_APP_DIR_PATH")"
zip -r app-simulator.zip "$(basename "$BITRISE_APP_DIR_PATH")"
BUILD_FILE="$PWD/app-simulator.zip"
```
and use `"platform": "ios"` when creating the build record.
Supported file types are `.apk` and `.aab` for Android, and `.zip` or
`.tar.gz` containing your `.app` simulator build for iOS. Maximum file size is
4GB. See the [Application Builds API](/api-reference/application-builds) for
full request and response details.
## Web applications
If you use Bitrise for a web app, trigger a test plan with a single request:
```yaml theme={null}
- script@1:
title: Trigger QA.tech tests
inputs:
- content: |
#!/usr/bin/env bash
set -euo pipefail
curl -sSf -X POST "https://api.qa.tech/v1/run" \
-H "Authorization: Bearer $QATECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"testPlanShortId": "pln_abc123"}'
```
See the [Start Run API](/api-reference/start-run) for all available options, including environment URL overrides for staging or preview deployments.
## Blocking mode
To fail the Bitrise build when tests fail (for example as a release gate), poll the run status after starting it:
```bash theme={null}
# Start run and capture shortId
RUN_RESPONSE=$(curl -sSf -X POST "https://api.qa.tech/v1/run" \
-H "Authorization: Bearer $QATECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"testPlanShortId\": \"$TEST_PLAN_ID\",
\"applications\": [{
\"applicationShortId\": \"$APP_ID\",
\"environment\": { \"applicationBuildShortId\": \"$BUILD_SHORT_ID\" }
}]
}")
SHORT_ID=$(echo "$RUN_RESPONSE" | jq -r '.run.shortId')
# Poll until completion
while true; do
STATUS_RESPONSE=$(curl -sSf "https://api.qa.tech/v1/run/$SHORT_ID" \
-H "Authorization: Bearer $QATECH_API_TOKEN")
STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status')
if [[ "$STATUS" == "COMPLETED" || "$STATUS" == "ERROR" || "$STATUS" == "CANCELLED" ]]; then
RESULT=$(echo "$STATUS_RESPONSE" | jq -r '.result')
[[ "$RESULT" == "PASSED" ]] && exit 0 || exit 1
fi
sleep 30
done
```
See the [Run Status API](/api-reference/run-status) for polling details and error handling. If the polling step might exceed your step timeout, raise the step's timeout in the Bitrise Workflow Editor.
## Related Documentation
* **[CI/CD Integration](/configuration/ci-cd-integration)** - Overview of integration modes
* **[Application Builds API](/api-reference/application-builds)** - Full build upload reference
* **[Start Run API](/api-reference/start-run)** - Complete API documentation
* **[Mobile App Testing](/test-features/mobile-app-testing)** - Mobile testing concepts and setup
* **[Test Plans](/core-concepts/test-plans)** - Create and organize test plans
# CI/CD Integration
Source: https://docs.qa.tech/configuration/ci-cd-integration
Integrate QA.tech into your CI/CD workflows to automatically test your applications on every deployment, pull request, or scheduled run.
## Two Ways to Integrate
QA.tech offers two integration modes for CI/CD:
| Feature | API-Driven Testing | AI Exploratory Testing |
| :----------------- | :-------------------------------------------------------------------------------------- | :-------------------------------------- |
| **How it works** | You define test plans, trigger via API | AI analyzes PR, creates & runs tests |
| **CI/CD Support** | Any system (GitLab, GitHub, Bitrise, Bitbucket, Azure DevOps, CircleCI, Jenkins) | GitHub App and GitLab integration |
| **Test Selection** | You choose which test plan to run | AI selects relevant tests automatically |
| **Test Creation** | You create tests in QA.tech UI or [via API](/api-reference/test-cases/create-test-case) | AI creates tests for new functionality |
| **Best For** | Regression testing, scheduled runs, deployment gates | Exploratory testing of new features |
**Which mode should you use?** - **API-driven**: Use for regression testing,
scheduled test runs, or when you want full control over which tests run - **AI
exploratory**: Use GitHub App or GitLab integration when you want AI to
discover and test new functionality automatically
## API-Driven Testing
Works with any CI/CD system that can make HTTP requests. You create test plans in QA.tech, then trigger them programmatically from your pipeline using a simple REST API call. This approach gives you full control over which tests run, when they run, and which environments to test - perfect for regression testing, deployment gates, and scheduled test suites.
### How It Works
1. **Create test plans** in QA.tech - organize your test cases into logical groups
2. **Trigger via REST API** from your CI/CD pipeline
3. **Test runs execute** against your configured environments
4. **Results available** in QA.tech dashboard and via API
### What You Can Do
* **Test preview/staging environments** - Override application URLs dynamically when testing Vercel, Netlify, or custom preview deployments. See [Preview Environments](/core-concepts/applications-and-environments#preview-environments).
* **Override device configurations** - Test with different device presets per-run via API, allowing you to test mobile, tablet, and desktop configurations without creating separate test plans. See [Start Run API](/api-reference/runs/start-test-run) for `devicePresetShortId` parameter details.
* **Block deployments until tests pass** - Wait for test results before proceeding with your pipeline. The GitHub Action has built-in blocking support; other platforms can poll the [Run Status API](/api-reference/runs/get-run) to wait for completion.
* **Schedule recurring test runs** - Set up nightly regression suites or periodic smoke tests using your CI/CD system's cron functionality.
* **Custom Slack notifications** - Send test results to specific Slack channels per-run, separate from your project's default channel. See [Notifications](/core-concepts/notifications).
* **Run specific test plans** - Target regression suites, smoke tests, or full test suites by specifying the test plan short ID.
* **Custom post-run automation** - Use the [Run Status API](/api-reference/runs/get-run) to trigger webhooks, update status pages, or send custom alerts when runs complete.
### Get Started
QA.tech works with any CI/CD platform that can make HTTP requests. We provide detailed guides for:
* **[GitHub Actions](/configuration/github-actions)** - Official GitHub Action with built-in blocking mode
* **[GitLab CI](/configuration/gitlab)** - GitLab CI/CD pipeline integration via API
* **[Bitrise](/configuration/bitrise)** - Mobile CI/CD: upload Android/iOS builds and run mobile test plans
**Other platforms** (Bitbucket Pipelines, Azure DevOps, CircleCI, Jenkins, and more) can integrate using the REST API directly. See the [Start Run API Reference](/api-reference/runs/start-test-run) for complete API documentation and examples.
## AI Exploratory Testing (GitHub and GitLab)
QA.tech exploratory review integrations automatically analyze pull/merge requests, identify user-facing changes, and create tests for new functionality. They run those tests against your preview deployment context and post results back to the request discussion. This helps catch issues before merge without requiring you to manually write tests for every feature.
### How It Works
1. **Analyzes request changes** - Examines code diff and identifies user-facing changes
2. **Selects relevant tests** - Finds existing tests that apply to the changes
3. **Creates missing tests** - Generates tests for untested functionality
4. **Runs tests** - Executes against PR preview deployment
5. **Posts review** - Comments on the request with test results and approval/rejection
### Ways to trigger a change review
| Trigger | Best for | Posts review to PR/MR? |
| :---------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- |
| **[GitHub App](/configuration/github-app)** (automatic) | Default for GitHub: every PR open/sync triggers a review, with environments resolved from the App's mapping | Yes (native PR review) |
| **[GitLab MR integration](/configuration/gitlab)** (automatic) | GitLab teams with native deployment signals or `@qa.tech` comments | Yes (MR review note) |
| **[Change Review Action](/configuration/github-actions#change-review-action)** (CI) | Custom flows on top of the GitHub App: multiple applications per PR, label-gated triggers, deploy-job ordering | Yes (same native PR review the App posts). Creates the **QA.tech / PR Review** check even when auto-run is off. |
| **[Start change review chat API](/api-reference/chat/start-change-review-chat)** | GitLab CI or any pipeline that can make HTTP requests | Yes when invoked with `mode: "pr"` and a connected GitHub/GitLab repo. Same explicit check behavior as the Change Review Action on GitHub. |
### Requirements
* Connected VCS integration: the [GitHub App](/configuration/github-app) (for GitHub triggers, including the Change Review Action) or the [GitLab integration](/configuration/gitlab) (for GitLab triggers)
* Repository selected in **Settings → Integrations** so the review agent has permission to read the PR/MR
* Preview deployment context available: native deployment signals, a manual `@qa.tech` comment with the preview URL, or `applications_config` supplied to the action/API
All four triggers run the same review agent and post the same native PR/MR
review. The difference is who detects the PR and what you can customize around
it. Use the automatic GitHub App or GitLab integration for zero-config reviews
on every PR/MR; reach for the Change Review Action or API when you need custom
orchestration on top of that integration (e.g. routing application short IDs
to specific URLs per PR, or gating reviews behind a deploy job).
## Related Documentation
* **[Test Plans](/core-concepts/test-plans)** - Create and organize test plans to run via API
* **[API Reference](/api-reference/runs/start-test-run)** - Complete API documentation with all parameters
* **[Preview Environments](/core-concepts/applications-and-environments#preview-environments)** - Testing dynamic preview deployments
# Cloudflare WAF & Turnstile
Source: https://docs.qa.tech/configuration/cloudflare-waf-turnstile
Allow QA.tech through Cloudflare edge blocking (WAF) and app-embedded Turnstile widgets
Cloudflare can block QA.tech tests in **two different ways**. They look similar to testers (“something stopped the page”) but are configured in **different places** and need **different fixes**.
## Two types of Cloudflare blockers
| | **1. Edge blocking (WAF / firewall)** | **2. Cloudflare Turnstile (in your app)** |
| ------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| **What you see** | The **whole page** fails to load—403/1020, “Access denied”, JS challenge, or an interstitial before your HTML | Your app loads, but a small **“Verify that you are human”** widget blocks a form or action |
| **Who controls it** | **You**, in the [Cloudflare dashboard](https://dash.cloudflare.com) (WAF, firewall rules, Bot Fight Mode, etc.) | **You**, in **your application code**—you add the Turnstile widget and validate tokens on your server |
| **Fixed by** | WAF custom rules, IP allowlists, verified-bot skips (this page, [§ WAF](#waf-and-firewall-rules)) | Test sitekeys, staging config, or server-side bypass—not WAF rules ([§ Turnstile](#cloudflare-turnstile-in-your-application)) |
**WAF and firewall rules do not remove an embedded Turnstile widget.** If
users see the small human-verification box on your login or checkout form,
that is application Turnstile—you must change how your app loads or validates
Turnstile, not only Cloudflare Security settings.
QA.tech traffic identifies as [**QATechBot**](/bot). Use the sections below based on which blocker you hit.
***
## WAF and firewall rules
When Cloudflare blocks at the **edge**, the browser often never receives your app—or gets a Cloudflare error page instead. Configure this in the dashboard under **Security → WAF** (custom rules, managed rules) and related firewall / bot settings.
On the **Free plan**, you cannot allow a single verified bot—you can only
allow **all** verified bots at once, or match traffic another way. See [Free
plan limitations](#free-plan-limitations) below.
### Approaches at the edge
| Approach | Best for | Security |
| ------------------------------------------------ | ----------------------------------------------- | ----------------------------------- |
| Allow all verified bots (`cf.client.bot`) | Free plan; QA.tech is a Cloudflare-verified bot | High—Cloudflare validates bot IPs |
| User-Agent + verified bot | Free plan; only QATechBot, when verified | Highest for a single bot |
| User-Agent only | Allow only QATechBot by name | Lower—User-Agent can be spoofed |
| [IP allowlist](/configuration/ip-access-control) | Any plan; works without verified bot status | High when combined with reverse DNS |
### Free plan limitations
Cloudflare exposes different bot signals by plan:
| Signal | Plan | What it does |
| ------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------- |
| `cf.client.bot` | Free | Boolean: `true` or `false`. Matches **all** verified bots or **none**. You cannot target one bot. |
| `cf.bot_management.verified_bot` + `cf.verified_bot_category` | Enterprise | Target specific bot categories or individual bots. |
So on Free you either allow every Cloudflare-verified bot, match **QATechBot** by User-Agent, or combine User-Agent with `cf.client.bot` when QA.tech traffic is verified.
See [Verifying QATechBot traffic](/bot#verifying-qatechbot-traffic) for IP allowlists, reverse DNS, and Web Bot Auth.
### Option 1: Allow all verified bots (recommended on Free)
Create a WAF custom rule that skips remaining rules for any Cloudflare-verified bot (including QATechBot when verified):
In the Cloudflare dashboard, go to **Security → WAF → Custom rules**.
**Expression:**
```
(cf.client.bot)
```
**Action:** **Skip** → **All remaining custom rules**
**Place at:** **First** (top of the list).
Save the rule. Changes usually propagate within a few minutes.
This lets Googlebot, Bingbot, monitoring bots, QATechBot (when verified), and other Cloudflare-verified bots through without hitting your other custom WAF rules.
### Option 2: Allow QATechBot by User-Agent only
If you only want to allow traffic that identifies as QATechBot (and accept Free-plan limits on per-bot verification):
**Expression:**
```
(http.user_agent contains "QATechBot")
```
**Action:** **Skip** → **All remaining custom rules**
**Place at:** **First**
User-Agent strings are easy to spoof. Any client can send `User-Agent:
...QATechBot...`. This is less secure than `cf.client.bot`, which checks the
client against Cloudflare’s verified bot IP data.
The canonical suffix is documented on the [QA.tech Bot](/bot) page.
### Option 3: User-Agent and verified bot (best security on Free)
Require both the QATechBot User-Agent **and** Cloudflare verified-bot status:
**Expression:**
```
(http.user_agent contains "QATechBot" and cf.client.bot)
```
**Action:** **Skip** → **All remaining custom rules**
**Place at:** **First**
The request must claim to be QATechBot **and** pass Cloudflare’s verified-bot check. Use this when QA.tech egress is recognized as a verified bot.
### Enterprise: target specific bots
On **Enterprise**, you can use Bot Management fields instead of allowing all verified bots:
* `cf.bot_management.verified_bot`
* `cf.verified_bot_category`
Use Cloudflare’s bot category documentation to build rules that allow only the categories that include QATechBot, without opening all verified bots.
***
## Cloudflare Turnstile in your application
**Turnstile** is a CAPTCHA-style widget **you embed** in HTML (login, signup, checkout, etc.). It is **not** configured with WAF custom rules. QA.tech cannot “skip” production Turnstile from the Cloudflare dashboard alone—you need an application-level strategy.
### What testers usually see
* The rest of the page renders normally.
* A compact **“Verify that you are human”** (or invisible) challenge sits on the form QA.tech is trying to submit.
* Tests fail on submit or timeout waiting for the widget.
### What to do instead
Pick one or combine approaches below. All of these are **application changes**—not Cloudflare dashboard settings.
### Bypass Turnstile with a shared header
On staging or other non-production URLs shared with QA.tech, skip rendering and validating Turnstile when a request carries a secret header only your team and QA.tech know.
1. Store a secret in your environment (e.g. `CAPTCHA_BYPASS_SECRET`)—never commit it or expose it in client-side code.
2. In your app, **do not render** the widget and **skip server-side siteverify** when the header matches:
```javascript theme={null}
const PRE_SHARED_SECRET = process.env.CAPTCHA_BYPASS_SECRET
function shouldBypassTurnstile(headers) {
return headers['x-bypass-captcha'] === PRE_SHARED_SECRET
}
// When rendering the page
if (shouldBypassTurnstile(request.headers)) {
// doNotRender() — omit the Turnstile script/widget
} else {
// render Turnstile as usual
}
// When handling form submit
if (shouldBypassTurnstile(request.headers)) {
// skip Turnstile siteverify
} else {
// verify cf-turnstile-response with Cloudflare
}
```
3. In QA.tech, send the same header on every request—for example via [**Custom Headers** on a device preset](/test-features/device-presets#custom-headers):
| Header | Value |
| ------------------ | ---------------------------------- |
| `x-bypass-captcha` | Your `CAPTCHA_BYPASS_SECRET` value |
Treat the secret like a password. Use it only on environments where bypass is
acceptable, rotate it if leaked, and never enable this check in production
unless you fully accept the risk.
### Allow QA.tech traffic server-side
If you prefer not to use a shared header, bypass Turnstile only when you can confidently identify QA.tech—for example:
* Request comes from a [QA.tech allowlisted IP](/configuration/ip-access-control)
* `User-Agent` contains `QATechBot` (see [QA.tech Bot](/bot)) **and** you validate IP or use other checks—not User-Agent alone on production
Apply the same **do not render** and **skip siteverify** logic as the header approach, using your server-side detection instead of `x-bypass-captcha`.
### Use Turnstile test keys (non-production)
Cloudflare provides [dummy sitekeys and secret keys](https://developers.cloudflare.com/turnstile/troubleshooting/testing/) for automated testing—no real challenge, predictable pass/fail.
For example, sitekey `1x00000000000000000000AA` with secret
`1x0000000000000000000000000000000AA` always validates successfully.
In staging, preview, or QA.tech target environments, render the **test**
sitekey in your frontend and validate with the matching **test** secret on
your server. Keep production keys only in production.
Production secret keys **reject** dummy tokens from test sitekeys. You must
use the paired test secret key in the environment where test sitekeys are
rendered.
Do **not** rely on WAF skip rules or verified-bot expressions to clear an in-app Turnstile widget on production keys—those only affect **edge** security.
***
## Related
User-Agent format, IP verification, and Web Bot Auth
Allowlist QA.tech egress IPs (helps edge blocking, optional app bypass)
**External:** [Cloudflare WAF custom rules](https://developers.cloudflare.com/waf/custom-rules/), [Verified bots](https://developers.cloudflare.com/bots/get-started/verified-bots/), [Turnstile testing](https://developers.cloudflare.com/turnstile/troubleshooting/testing/)
# Envoyer
Source: https://docs.qa.tech/configuration/envoyer
Trigger your test from a GitHub repo
## Envoyer
Envoyer is a zero-downtime PHP deployment tool created by the makers of Laravel. It allows you to deploy PHP and Laravel applications without downtime, supporting integrations with GitHub, Bitbucket, GitLab, Slack, and other services. Features include seamless deployment rollbacks, application health checks, cron job monitoring, and deployment to multiple servers. It offers various plans starting at \$10 per month, allowing unlimited team members and projects depending on the tier.
## Envoyer Integration
Connecting QA.tech to your Envoyer setup is simple. Follow these steps.
Inside Envoyer, find your project and navigate to the section Deployment Hooks, and press "Add Hook"
Fill in the modal with the following values:
Name: `Trigger QA.tech Tests`
Script: Fetch this script from QA.tech
The script are found in Congiguration / Integration at QA.tech
It should show up in the list of hooks
# GitHub Actions
Source: https://docs.qa.tech/configuration/github-actions
Integrate QA.tech testing and PR reviews into GitHub Actions workflows
The [`QAdottech/run-action`](https://github.com/QAdottech/run-action) repository ships two GitHub Actions you can use from any workflow:
| Action | Path | What it does |
| :-------------------------------------------- | :-------------------------------------- | :-------------------------------------------------------------------------------------------------- |
| [Test Run Action](#test-run-action) | `QAdottech/run-action@v3` | Trigger a specific test plan on demand. Full control over which tests run and against which URL. |
| [Change Review Action](#change-review-action) | `QAdottech/run-action/change-review@v3` | Trigger a QA.tech autonomous change review on a pull request and (optionally) wait for the verdict. |
**Picking an integration mode:**
* Use the **Test Run Action** for regression suites, scheduled runs, and deployment gates where you decide which tests run.
* Use the **Change Review Action** when the [GitHub App's](/configuration/github-app) automatic PR review is not flexible enough. Common cases: projects with more than one application per PR (where the App's environment mapping is fiddly), workflows that need to trigger reviews on labels or after a specific deploy job, or pipelines that need to route specific application short IDs to specific preview URLs per PR.
Both actions live in the same [`QAdottech/run-action`](https://github.com/QAdottech/run-action) repo. The Change Review Action calls the same review agent the GitHub App runs and **requires the GitHub App to be installed at the organization level with access to the repository** so the agent can read and review the PR. Unlike the App's automatic trigger, it does **not** require you to map the repository to a project in QA.tech (the action passes the repo and preview URLs inline); it just lets you drive the review from CI instead of (or alongside) the App's automatic trigger.
See [CI/CD Integration](/configuration/ci-cd-integration) for the broader picture.
## Test Run Action
Trigger a test plan from a workflow. Use this when you want full control over which tests run, when, and where.
### Setup
Add a secret to your GitHub repository (**Settings → Secrets and variables → Actions**):
* `QATECH_API_TOKEN` - Your QA.tech API token
Find it in your [project settings](https://app.qa.tech/current-project/settings/integrations).
Create `.github/workflows/qatech.yml`:
```yaml theme={null}
name: QA.tech Tests
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
blocking: true
```
### Implementation patterns
#### Run on pull requests
```yaml theme={null}
name: PR Tests
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
test_plan_short_id: 'smoke-tests'
blocking: true
```
#### Test preview deployments
```yaml theme={null}
name: Test Preview
on:
pull_request:
types: [opened, synchronize]
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
preview_url: ${{ steps.deploy.outputs.url }}
steps:
- name: Deploy to Vercel
id: deploy
run: |
# Your deployment logic
echo "url=https://preview-${{ github.event.pull_request.number }}.vercel.app" >> $GITHUB_OUTPUT
test:
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
test_plan_short_id: 'regression-suite'
blocking: true
applications_config: |
{
"applications": {
"frontend-app": {
"environment": {
"url": "${{ needs.deploy.outputs.preview_url }}",
"name": "PR-${{ github.event.pull_request.number }}"
}
}
}
}
```
#### Scheduled testing
```yaml theme={null}
name: Nightly Tests
on:
schedule:
- cron: '0 2 * * *' # Daily at 2 AM UTC
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
test_plan_short_id: 'full-regression'
blocking: false
```
#### Use action outputs
```yaml theme={null}
- uses: QAdottech/run-action@v3
id: qatech
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
blocking: true
- name: Check Results
if: steps.qatech.outputs.run_result == 'FAILED'
run: echo "Tests failed! See ${{ steps.qatech.outputs.run_url }}"
```
### Test Run Action reference
#### Inputs
| Input | Description | Required | Default |
| :-------------------- | :------------------------------------------------------------ | :------- | :-------------------- |
| `api_token` | QA.tech API token | Yes | - |
| `test_plan_short_id` | Test plan short ID to run | No | All tests |
| `blocking` | Wait for test results before completing | No | `false` |
| `applications_config` | JSON with application environment and device preset overrides | No | - |
| `api_url` | Custom API URL | No | `https://api.qa.tech` |
#### Outputs
| Output | Description |
| :------------- | :-------------------------------------------------------------------------------------------- |
| `run_created` | Whether the test run was created successfully |
| `run_short_id` | The short ID of the run |
| `run_url` | The URL of the run |
| `run_status` | Final status (`COMPLETED`, `ERROR`, `CANCELLED`, or `TIMED_OUT`) - only when `blocking: true` |
| `run_result` | Test result (`PASSED`, `FAILED`, `SKIPPED`) - only when `blocking: true` |
## Change Review Action
Trigger a QA.tech autonomous change review on a pull request directly from your workflow. The action calls [`POST /v1/chat/change-review`](/api-reference/chat/start-change-review-chat) with the PR URL and your per-PR environment overrides. QA.tech then runs the same review agent the [GitHub App](/configuration/github-app) uses: it selects relevant tests, fills coverage gaps, runs everything against your preview, and posts a native PR review on GitHub when it's done. The action exposes the chat conversation URL as a workflow output, and (in blocking mode) the agent's final assistant message and status.
The Change Review Action is an **explicit** trigger. It starts a review and
creates the **QA.tech / PR Review** GitHub check even when **Auto-run on PRs**
is disabled in the GitHub App integration. See [PR Review check on
GitHub](/configuration/github-app#pr-review-check-on-github) for how check
states differ between automatic, draft, and CI-driven reviews.
### Setup
The Change Review Action runs the same agent as the GitHub App, so the GitHub App connection needs to be installed at the organization level with access to the repository. You do **not** need to map the repository to a project in QA.tech (that mapping only powers the App's automatic trigger); the action passes the repo and preview URLs inline. The action then drives that agent from CI.
If you haven't already, follow [Install GitHub App](/configuration/github-app#how-to-set-it-up) to connect the App at the organization level under **Settings → Organization → Connections** and grant it access to the repository you want reviewed. Without the App connection (or repo access), the action's chat will start but the agent has no permission to read your PR. You do **not** need to create a repository mapping under **Settings → Integrations → GitHub App** for the action; that mapping is only needed for the App's automatic PR trigger.
This step only applies if you have also mapped the repository under **Settings
→ Integrations → GitHub App** to enable the App's automatic trigger. If you
want the action to be the only thing that triggers reviews on this repo, open
that page and turn off **Auto-run on PRs**. With auto-run off, QA.tech does
not create a **QA.tech / PR Review** check on PR open; the check appears when
this action (or a `@qa.tech` comment) runs. Leave auto-run on if you want both
the automatic trigger and on-demand action runs. If you never mapped the
repository, there is no automatic trigger to disable.
Reuse the `QATECH_API_TOKEN` secret from the Test Run Action setup. The Change
Review Action does not need `project_id`; your API token already scopes the
request to a project.
Open **Test Plans → API Integration** in QA.tech to see which applications
belong to your project. Each application has a short ID (e.g. `app_abc123`)
that you pass in `applications_config`. Projects with more than one
application should pass one entry per app so the agent knows which URL to test
against.
Create `.github/workflows/qatech-change-review.yml`:
```yaml theme={null}
name: QA.tech Change Review
on:
pull_request:
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action/change-review@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
applications_config: |
{
"applications": {
"YOUR_APP_SHORT_ID": {
"environment": {
"url": "https://preview-${{ github.event.number }}.example.com"
}
}
}
}
```
On `pull_request` events the action reads the PR URL from `github.event.pull_request.html_url` automatically, so `pr_url` is optional.
### Implementation patterns
#### Block the workflow until the review finishes
By default the action returns as soon as the chat conversation is created and the agent starts working. Set `blocking: true` to wait for the agent to finish (and post its native PR review) before the workflow step completes. The step fails when the review ends in `FAILED` or `CANCELLED`, which makes it suitable as a deployment gate.
```yaml theme={null}
- uses: QAdottech/run-action/change-review@v3
id: review
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
blocking: true
applications_config: |
{
"applications": {
"YOUR_APP_SHORT_ID": {
"environment": {
"url": "https://preview-${{ github.event.number }}.example.com"
}
}
}
}
- name: Log conversation
if: always()
run: |
echo "Status: ${{ steps.review.outputs.chat_status }}"
echo "Conversation URL: ${{ steps.review.outputs.chat_url }}"
echo "Final assistant message: ${{ steps.review.outputs.chat_response }}"
```
The native review is posted to the PR by the agent itself. `chat_response` is the agent's final assistant text in the chat (which may summarize what it did but is not the GitHub review body); use it for logging or further automation, not as a replacement for the PR review.
#### Post-merge change review (no preview environments)
If you do not have per-PR preview environments, run the change review **after** merging to your main branch. The workflow deploys to staging/production, extracts the PR number from the merge commit, and reviews the merged PR's changes against the deployed environment. Results are posted back on the merged PR as a comment.
```yaml theme={null}
name: Deploy and Review
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Your existing deploy steps here
- run: echo "Deploy to staging..."
change-review:
needs: deploy
runs-on: ubuntu-latest
steps:
- name: Extract PR number from merge commit
id: pr
env:
COMMIT_MSG: ${{ github.event.head_commit.message }}
run: |
PR_NUMBER=$(echo "$COMMIT_MSG" | grep -oE '\(#[0-9]+\)' | tail -1 | grep -oE '[0-9]+')
if [ -n "$PR_NUMBER" ]; then
echo "url=https://github.com/${{ github.repository }}/pull/$PR_NUMBER" >> "$GITHUB_OUTPUT"
echo "found=true" >> "$GITHUB_OUTPUT"
fi
- name: Start QA.tech change review
if: steps.pr.outputs.found == 'true'
uses: QAdottech/run-action/change-review@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
pr_url: ${{ steps.pr.outputs.url }}
context: 'This PR has already been merged and deployed. Test the changes and report any issues found as a comment.'
applications_config: |
{
"applications": {
"YOUR_APP_SHORT_ID": {
"environment": {
"url": "https://staging.example.com"
}
}
}
}
```
Notes:
* The PR number is extracted from GitHub's default merge/squash commit message format (`(#123)`).
* Direct pushes to `main` without a PR are skipped automatically (the review step is gated on `steps.pr.outputs.found`).
* Results are posted back on the merged PR as a comment, since the PR is already closed.
#### Route multiple applications to per-PR preview URLs
The most common reason to reach for this action over the GitHub App's automatic trigger: projects with more than one application per PR (for example a frontend, a backend, and an admin dashboard). Pass one entry per application so the agent knows which preview URL to use for each. Each entry must include an `environment` with at least one of `url`, `shortId`, or `applicationBuildShortId`.
```yaml theme={null}
- uses: QAdottech/run-action/change-review@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
applications_config: |
{
"applications": {
"app_frontend": {
"environment": {
"url": "https://preview-${{ github.event.number }}-web.example.com",
"name": "PR-${{ github.event.number }}"
}
},
"app_backend": {
"environment": {
"url": "https://preview-${{ github.event.number }}-api.example.com",
"name": "PR-${{ github.event.number }}"
}
}
}
}
```
#### Override the device preset
Pass `devicePresetShortId` alongside `environment` to run the review against a specific [device preset](/test-features/device-presets):
```yaml theme={null}
applications_config: |
{
"applications": {
"app_frontend": {
"environment": { "url": "https://preview.example.com" },
"devicePresetShortId": "preset_mobile_abc123"
}
}
}
```
#### Add free-form context
Use the `context` input to guide the review with PR-specific or repo-wide instructions:
```yaml theme={null}
- uses: QAdottech/run-action/change-review@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
context: |
Focus on the new Apple Pay flow. Test login is shared with the existing
checkout test and uses test-user@example.com.
applications_config: |
{ "applications": { "YOUR_APP_SHORT_ID": { "environment": { "url": "https://preview.example.com" } } } }
```
#### Review a different PR
Set `pr_url` when the workflow runs outside a `pull_request` event (for example on `workflow_dispatch`):
```yaml theme={null}
on:
workflow_dispatch:
inputs:
pr_url:
description: PR to review
required: true
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: QAdottech/run-action/change-review@v3
with:
api_token: ${{ secrets.QATECH_API_TOKEN }}
pr_url: ${{ inputs.pr_url }}
applications_config: |
{ "applications": { "YOUR_APP_SHORT_ID": { "environment": { "url": "https://staging.example.com" } } } }
```
### Change Review Action reference
#### Inputs
| Input | Description | Required | Default |
| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------- | :------------------------- |
| `api_token` | QA.tech API token | Yes | - |
| `applications_config` | JSON of `{ "applications": { "": { "environment": { ... }, "devicePresetShortId": "..." } } }`. Each application must include an `environment`. | Yes | - |
| `blocking` | Wait for the assistant reply and expose it as an output. Fails the step on `FAILED` or `CANCELLED`. | No | `false` |
| `context` | Free-form context appended to the review. | No | - |
| `pr_url` | Pull request URL to review. Defaults to the PR URL of the current `pull_request` event. | No | Auto-detected on PR events |
| `api_url` | Custom API URL. | No | `https://api.qa.tech` |
Each environment in `applications_config` must include one of:
* `url` (plus optional `name`) to point the application at a preview URL.
* `shortId` to reference an existing environment short ID.
* `applicationBuildShortId` to reference a previously uploaded mobile build.
See [Start change review chat API](/api-reference/chat/start-change-review-chat) for the request schema this maps to.
#### Outputs
The agent posts its review natively on the PR. These outputs let you link to or log the chat conversation from your workflow; they are not the PR review body.
| Output | Description |
| :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chat_created` | `"true"` when the chat conversation was created successfully. |
| `chat_short_id` | Short ID of the change review chat conversation (e.g. `chat_abc123`). |
| `chat_url` | Dashboard URL of the chat. Always set, even when not blocking. |
| `chat_status` | Final status of the agent's assistant message: `COMPLETED`, `FAILED`, `CANCELLED`, or `TIMED_OUT`. Only set when `blocking: true`. |
| `chat_response` | The agent's final assistant text in the chat (free-form, may summarize what was done). Not the GitHub PR review body. Only set when `blocking: true`. |
### How polling works
When `blocking: true`, the action polls [`GET /v1/chat/{chat_short_id}`](/api-reference/chat/get-chat-conversation) every 20 seconds until the latest assistant message reaches a terminal status:
| Assistant status | Action behavior |
| :--------------------- | :----------------------------------------------------------------------- |
| `INITIATED` | Continues polling. |
| `PARTIAL` | Continues polling. |
| `COMPLETED` | Sets `chat_status` and `chat_response`. Step succeeds. |
| `FAILED` | Sets `chat_status` and `chat_response`. Step fails via `core.setFailed`. |
| `CANCELLED` | Sets `chat_status` and `chat_response`. Step fails via `core.setFailed`. |
| *(60 minutes elapsed)* | Sets `chat_status` to `TIMED_OUT`. Step fails via `core.setFailed`. |
Polling stops after an internal 60-minute timeout. Set a shorter [`timeout-minutes`](https://docs.github.com/en/actions/using-jobs/using-conditions-to-control-job-execution#jobsjob_idstepstimeout-minutes) on the step if you need a tighter cap.
## Direct API alternatives
If you prefer `curl` over the Actions:
* Test runs: use the [Start Run API](/api-reference/runs/start-test-run) to trigger runs and the [Run Status API](/api-reference/runs/get-run) for polling.
* Change reviews: use the [Start change review chat API](/api-reference/chat/start-change-review-chat) and poll with [Get chat conversation](/api-reference/chat/get-chat-conversation) until the latest assistant message reaches `COMPLETED`.
## Related documentation
* **[CI/CD Integration](/configuration/ci-cd-integration)** - Overview of integration modes
* **[GitHub App](/configuration/github-app)** - AI-powered automatic PR reviews via the GitHub App
* **[GitLab](/configuration/gitlab)** - Same patterns for GitLab CI and merge request reviews
* **[Start Run API](/api-reference/runs/start-test-run)** - Complete test-run API documentation
* **[Start change review chat API](/api-reference/chat/start-change-review-chat)** - Complete change review API documentation
* **[Notifications](/core-concepts/notifications)** - Slack notification configuration
# GitHub App for PR Reviews
Source: https://docs.qa.tech/configuration/github-app
Automatic AI-powered pull request testing and reviews
Get autonomous AI-powered test coverage and reviews on every pull request. The QA.tech GitHub App uses **AI exploratory testing** to analyze code changes, create missing tests, and post comprehensive reviews based on test results.
**Integration modes:**
* **AI exploratory** (this page): Automatic AI-powered PR reviews - GitHub only
* **API-driven**: Manual test plan triggers via [GitHub Actions](/configuration/github-actions) or [GitLab CI](/configuration/gitlab)
See [CI/CD Integration Overview](/configuration/ci-cd-integration) to understand both modes and choose the right approach for your needs.
If you use GitLab merge requests, see [GitLab Merge Request Reviews](/configuration/gitlab).
## What It Does
| Feature | Description |
| :-------------------------------- | :----------------------------------------------------------------------------------- |
| ✅ **Intelligent Test Selection** | AI semantically matches PR changes to relevant tests (typically 5-15 tests selected) |
| ✅ **Gap-Only Test Generation** | Creates 1-3 tests only when coverage gaps exist; most PRs create zero new tests |
| ✅ **Persistent Test Suite** | Auto-generated tests become permanent regression tests for future PRs |
| ✅ **Preview Environment Testing** | Tests against PR preview deployments |
| ✅ **Approval/Rejection** | Posts reviews with pass/fail verdicts |
| ✅ **Manual Trigger** | Comment `@qa.tech` on a PR to trigger, re-run, or steer a review on demand |
| ❌ **Backend-Only Testing** | Requires UI access - can't test microservices without frontend |
## How PR Reviews Work
```
PR opened/updated
↓
1. Classify Changes
→ User-facing? Continue
→ Docs/infra only? Skip testing, post info comment
↓
2. Assess Coverage
→ Find relevant existing tests
→ Identify gaps in coverage
↓
3. Create Tests (if needed)
→ Generate tests for untested functionality
→ Configure dependencies (e.g., login tests)
↓
4. Run Tests
→ Execute against PR preview environment
→ Wait for completion
↓
5. Post Review
→ ✅ Approve if all tests pass
→ ❌ Decline if tests fail
→ ℹ️ Informational if untestable
```
The agent posts reviews with:
* **Verdict:** Pass/fail/unable to verify
* **What was tested:** Description of coverage
* **Results summary:** Patterns and themes
* **Test details:** Table with individual test results
**Reviews focus on test results only** - no code quality opinions, implementation suggestions, or references to other bot comments.
## What the GitHub App Writes
The GitHub App needs write permissions to communicate test results back to your PR. Here is what it posts and when:
| Operation | When | Description |
| :--------------------------- | :-------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **PR comment (in progress)** | When the agent starts testing | Comment with link to the QA.tech conversation and details (branch, commit SHA, event type). Lets you track progress. |
| **PR comment (quota limit)** | When the organization is out of credits | Single comment with upgrade link. No tests run. |
| **PR review** | After tests complete | Native GitHub review with verdict (approve/request changes/comment), summary, test results table, and evaluation details. Replaces any pending review from the bot. |
| **Reaction (eyes emoji)** | When processing starts | Added to the triggering comment or PR to acknowledge it has been seen. |
| **Commit status** | During and after test run | GitHub check named **QA.tech / PR Review** on the PR commit. See [PR Review check on GitHub](#pr-review-check-on-github) below for when it appears and what each state means. |
If you see permission errors when installing the app, ensure the repository
grants write access for pull requests and statuses. The app only writes when
tests run or when the organization is out of credits.
## PR Review check on GitHub
QA.tech registers a GitHub check run named **QA.tech / PR Review** on PR commits. You can require it in branch protection as a merge gate. Whether a check appears on PR open or sync depends on your integration settings and how the review was triggered.
| Situation | Check created on PR open/sync? | What you see |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Auto-run on PRs** enabled (default) | Yes | `in progress` while the agent reviews, then a final verdict (approve, request changes, or comment) |
| **Auto-run on PRs** disabled | No | No check until you explicitly trigger a review (see below) |
| Draft PR and **Include draft PRs** disabled | Yes (skipped) | Skipped check titled **Draft PR** - auto-review does not run on drafts until the PR is marked ready for review, or until you comment `@qa.tech` |
| Comment `@qa.tech` on the PR | Yes (when the review starts) | `in progress`, then final verdict |
| [Change Review Action](/configuration/github-actions#change-review-action) or [Start change review chat API](/api-reference/chat/start-change-review-chat) | Yes (when triggered) | `in progress`, then final verdict. Runs even when **Auto-run on PRs** is off |
**Auto-run on PRs** controls only the automatic PR-open trigger. It does not block explicit triggers: `@qa.tech` comments, the Change Review Action, and the change-review API always start a review (and create or update the check) when invoked.
When **Auto-run on PRs** is off, QA.tech does **not** post a skipped check that says auto-review is disabled. The PR simply has no QA.tech check until you trigger a review manually or from CI.
### Integration settings
Configure these under **Settings → Integrations → GitHub App** for the repository:
| Setting | Effect on the PR Review check |
| :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Auto-run on PRs** | When on, creates the check on PR open/sync (subject to draft rules below). When off, no check on PR open - use `@qa.tech`, the Change Review Action, or the API. |
| **Include draft PRs** | When off (default), draft PRs get a skipped **Draft PR** check instead of an in-progress review. When on, draft PRs are reviewed the same as non-draft PRs (if auto-run is enabled). |
| **Register a commit check** | On by default. When off, QA.tech never posts the **QA.tech / PR Review** check for any trigger (auto-run, `@qa.tech`, the Change Review Action, or the API). Reviews still post the sticky PR comment and appear in the dashboard, but there is no merge gate to require. |
## Test Selection and Creation
### How Tests Are Selected
The agent uses AI to semantically match PR changes to test goals - not keyword matching.
**Example: PR changes checkout payment flow**
```
Your project: 645 total tests
Agent analyzes:
├─ "Complete checkout with credit card" → RELEVANT ✓
├─ "Complete checkout with PayPal" → RELEVANT ✓
├─ "Verify payment confirmation email" → RELEVANT ✓
├─ "User profile photo upload" → NOT RELEVANT ✗
└─ "Search products by category" → NOT RELEVANT ✗
Selected: 12 tests covering payment & checkout flows
```
**Selection mechanics:**
* Runs ALL tests it determines are relevant (no arbitrary limits)
* Only considers tests with `status='enabled'`
* More intelligent than running all tests every time
### When Tests Are Created
Tests are created only when coverage gaps exist, such as when you're developing a new feature not yet covered by your testing suite.
**Writing acceptance criteria?** Include test requirements in your Linear/Jira
ticket or PR description. The agent reads this context and uses it to create
more accurate tests for your new feature. See [Recommended Workflow for New
Features](/best-practices/creating-tests#recommended-workflow-for-new-features)
for details.
| PR Type | Existing Tests Selected | New Tests Created | Total |
| :--------------- | :---------------------- | :---------------------- | :---- |
| Bug fix in login | 3-7 | 0 (already covered) | 3-7 |
| Small feature | 5-10 | 1-2 (fill gaps) | 6-12 |
| Major feature | 15-25 | 3-5 (new functionality) | 18-30 |
| Refactor | 10-20 | 0 (no new behavior) | 10-20 |
| Docs/infra only | 0 | 0 (untestable via UI) | 0 |
**Example: PR adds Apple Pay to checkout**
```
Agent assesses existing coverage:
├─ "Complete checkout with credit card" ✓ Exists
├─ "Complete checkout with PayPal" ✓ Exists
└─ Apple Pay integration ✗ Gap identified
Decision: Create 1 new test
→ "Complete checkout with Apple Pay"
```
**Created tests:**
* Stay in your suite labeled 'ephemeral'
* Available for future PRs
* Manage in Settings → Test Cases (filter by 'ephemeral' to find them)
**Example lifecycle:**
```
PR #42: Add Apple Pay
├─ Agent creates "Complete checkout with Apple Pay"
├─ Test runs on PR #42 ✅
└─ Test persists with 'ephemeral' label
PR #58: Refactor checkout UI
├─ Agent finds existing test
├─ Runs it (no new test created) ✅
└─ Your suite now protects against Apple Pay regressions
```
**The agent prevents duplicates** by reading all existing tests first and using semantic deduplication. If one slips through, simply delete it in the UI.
**Self-limiting:** As your test suite grows, fewer tests are created automatically - better coverage means fewer gaps. Most PRs (bug fixes, refactors) create zero new tests.
**Mobile and Responsive Testing:** When a PR mentions mobile, tablet, or
responsive design changes, the AI agent may automatically test with
appropriate device presets. The agent detects keywords like "mobile",
"tablet", "responsive", or "Safari mobile" and can override device presets
when creating or running tests. See [Device
Presets](/test-features/device-presets) for configuring device settings.
## How to Set It Up
Go to [Settings → Organization → Connections](https://app.qa.tech/current-project/settings/organization/connections) and add the GitHub App connection. Follow the OAuth flow to grant access to your repositories.
Navigate to [Settings →
Integrations](https://app.qa.tech/current-project/settings/integrations?focus=github-app)
and select the repository you want to enable PR reviews for. **PR reviews are
enabled automatically** once you select a repository. **Optional:** Add review
context to guide the agent (e.g., "Focus on security vulnerabilities" or
"Validate accessibility standards"). This appears in the integration settings.
If you use Vercel, Netlify, Render, Railway, or Fly.io, your preview deployments are detected automatically - skip to step 4.
QA.tech automatically creates preview environment records in your project when it detects your preview deployments (from Vercel, Netlify, etc.). Your CI/CD platform handles the actual deployment - QA.tech just tests against the preview URLs. For more information, see [Preview Environments](/core-concepts/applications-and-environments#preview-environments).
**For CircleCI, Jenkins, GitLab CI, or custom CI/CD:**
QA.tech detects preview deployments using GitHub's [Deployments API](https://docs.github.com/en/rest/deployments/deployments). Your CI/CD needs to create deployment records after deploying.
**What QA.tech expects:**
1. GitHub deployment created for the PR commit SHA
2. Deployment status set to `success`
3. Status includes `target_url` field with your preview URL
**How to set this up:**
Your CI/CD makes two GitHub API calls after deployment:
1. [Create deployment](https://docs.github.com/en/rest/deployments/deployments#create-a-deployment) with commit SHA and environment name
2. [Create deployment status](https://docs.github.com/en/rest/deployments/statuses#create-a-deployment-status) with `state: "success"` and `target_url: "https://your-preview-url.com"`
**CI/CD platform docs for environment variables:**
* [CircleCI Environment Variables](https://circleci.com/docs/env-vars/) - Use `$CIRCLE_SHA1`, `$CIRCLE_PROJECT_USERNAME`, `$CIRCLE_PROJECT_REPONAME`
* [GitLab CI/CD Variables](https://docs.gitlab.com/ee/ci/variables/) - Use `$CI_COMMIT_SHA`, `$CI_PROJECT_NAMESPACE`, `$CI_PROJECT_NAME`
* [Jenkins Credentials](https://www.jenkins.io/doc/book/using/using-credentials/) - Use `$GIT_COMMIT` and custom variables
* [GitHub Actions](https://docs.github.com/en/actions/learn-github-actions/variables) - Use `${{ github.sha }}`, `${{ github.repository_owner }}`
Need help? Contact [support@qa.tech](mailto:support@qa.tech) with your CI/CD platform.
If you have multiple Applications (e.g., frontend + backend), map GitHub
deployment environments to the correct QA.tech Applications. **How it works:**
1. Your CI/CD deploys a PR and creates a GitHub environment (e.g., "Preview"
or "pr-123") 2. QA.tech detects the deployment 3. Tests run using the mapped
Application's URL from that environment **Location:** Settings → Integrations
→ GitHub App → Map Environments
Without GitHub deployments, QA.tech can't test against PR-specific URLs and
will use your default environment.
Once configured, the agent automatically runs on PRs:
1. Detects code changes when PRs are opened or updated
2. Determines which tests are relevant
3. Creates new tests for untested functionality
4. Runs all relevant tests against the PR preview
5. Posts a review with approval or decline based on results
## Triggering a PR Review
### Automatic trigger (simple)
With **Run automatically on PRs** enabled, QA.tech starts a review when a PR is opened or updated and its preview deployment is ready. This is the simplest option once a repository is configured.
Automatic triggering works best when you have a **single application**
deployed via **GitHub deployments** (or a provider that creates them, such as
Netlify or Vercel) and you don't need extra control over when reviews run. If
you have multiple applications per PR, don't use deployment-based previews, or
need to gate reviews on labels, paths, or a specific deploy job, use the
[GitHub Action trigger](#github-action-trigger-recommended) instead.
### GitHub Action trigger (recommended)
For the most control, trigger reviews from your CI with the [Change Review Action](/configuration/github-actions#change-review-action). It runs the same review agent and posts the same native PR review as the automatic trigger, but lets you decide exactly when it fires and pass an `applications_config` per PR - so you can route each application to its own preview URL, gate reviews on labels or paths, or order the review after a specific deploy job.
Turn off **Run automatically on PRs** in **Settings → Integrations → GitHub App** when driving reviews from the action, so each PR isn't reviewed twice. See [GitHub Actions](/configuration/github-actions#change-review-action) for setup details.
### Manual trigger with an `@qa.tech` comment
You can trigger a review at any time by commenting `@qa.tech` on the pull request. This works on the main PR conversation and on inline code review comments, even when **Run automatically on PRs** is turned off or a previous review was abandoned.
The mention is case-insensitive (`@QA.tech`, `@qatech`, and similar variants all match). When QA.tech picks up the comment, it reacts with an eyes emoji to acknowledge it.
| Comment | What happens |
| :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------ |
| `@qa.tech` | Re-runs the review. QA.tech waits for a ready preview deployment on the PR's latest commit, then starts automatically. |
| `@qa.tech test the checkout flow` | Runs a focused review. Any instructions after the mention are passed to the agent to steer what it tests. |
| `@qa.tech https://preview.example.com` | Runs immediately against the URL in the comment, which is treated as the preview deployment to test (skips waiting for a deployment). |
Without a URL in the comment, QA.tech waits for a ready preview deployment on
the PR's latest commit. It posts a short "waiting for a preview deployment"
comment and starts the review automatically once one is ready. Include a
preview URL in the comment to start immediately against that environment.
### Re-run from the GitHub checks UI
Every review posts a status check on the PR commit. You can re-trigger a review by using GitHub's **Re-run** button on the QA.tech check, the same way you re-run any other CI check.
## Common Questions
**Will auto-generated tests pollute my test suite?**
No. The agent only creates tests for coverage gaps - most PRs (bug fixes, refactors) create zero new tests. Even major features typically add 3-5 focused tests. The system is self-limiting: better coverage → fewer gaps → less generation.
**Can I control which tests run?**
The GitHub App selects tests autonomously, but you can steer a run by commenting `@qa.tech ` on the PR (e.g. `@qa.tech test the checkout flow`) - see [Triggering a PR Review](#triggering-a-pr-review). For full manual test selection, use [GitHub Actions](/configuration/github-actions) instead. You can use both: automatic PR reviews + manual deep testing on-demand.
**How do I trigger or re-run a review manually?**
Comment `@qa.tech` on the pull request. Add instructions to focus the review (`@qa.tech test the checkout flow`) or a preview URL to test a specific environment (`@qa.tech https://preview.example.com`). You can also use GitHub's **Re-run** button on the QA.tech check. See [Triggering a PR Review](#triggering-a-pr-review).
**My project has multiple applications per PR and the App's environment mapping isn't a good fit. What can I do?**
Install the GitHub App as usual, then turn off **Auto-run on PRs** in **Settings → Integrations → GitHub App** and drive reviews from CI with the [Change Review Action](/configuration/github-actions#change-review-action). The action runs the same review agent (and posts the same native PR review) but lets you pass an `applications_config` per PR, so you can route each application short ID to its own preview URL without relying on the App's static environment mapping. With auto-run off, no **QA.tech / PR Review** check appears on PR open; the check is created when the action runs.
**Can I gate reviews on labels, paths, or a specific deploy job?**
Yes. Use the [Change Review Action](/configuration/github-actions#change-review-action) instead of the automatic trigger and put the gating logic in your workflow (`if:` conditions on labels or paths, `needs:` on a deploy job, etc.). Disable **Auto-run on PRs** if you want only the action to fire; the action posts the native PR review the same way the App does.
**PR reviews aren't posting - what should I check?**
* Verify GitHub App is installed and has repository access
* Check repository is configured in QA.tech Settings → Integrations
* Ensure PR has user-facing changes (docs/infra-only PRs are skipped)
* If you see 403 Forbidden, check that the app has write permissions for pull requests and statuses
**We ran out of credits - what happens?**
QA.tech posts a single comment on the PR with an upgrade link. No tests run. No review is posted.
**Tests running against wrong URL?**
* Map GitHub environments to Applications in Settings → Integrations → GitHub App
* Verify your CI/CD creates GitHub deployment records
* Check environment names match between GitHub and QA.tech
**Agent created irrelevant tests?**
* Add review context with specific guidelines in integration settings
* Update existing tests to better cover the functionality
* The agent learns from your existing test patterns
## Related Documentation
* **[CI/CD Integration Overview](/configuration/ci-cd-integration)** - Learn about integration modes and capabilities
* **[GitHub Actions](/configuration/github-actions)** - API-driven testing with manual control over test plans
* **[Test Plans](/core-concepts/test-plans)** - Organize tests for API-driven runs
# GitLab Merge Request Reviews and CI/CD
Source: https://docs.qa.tech/configuration/gitlab
Set up automated GitLab merge request reviews and API-driven test runs
There are two independent ways to integrate GitLab with QA.tech. Pick the one that matches what you want to do — they use different setup pages and different credentials.
| | **A) GitLab CI** | **B) GitLab MR Reviews** |
| :----------------------------- | :------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- |
| **What it does** | Triggers QA.tech test plans from your CI/CD pipeline | Runs exploratory tests on merge requests and responds to `@qa.tech` |
| **Setup page** | [Integrations → GitLab CI](https://app.qa.tech/current-project/settings/integrations/gitlab) | Integrations → **GitLab MR Reviews** |
| **GitLab Connection (OAuth)?** | **Not required** | **Required** — so QA.tech can handle webhooks and post on MRs |
| **Credential** | QA.tech API key from Organization Settings → API Keys, stored as the GitLab CI/CD variable `QA_TECH_API_TOKEN` | The GitLab Connection identity (recommended: a dedicated **service user**) |
A GitLab **Connection** is only needed for path **B**. Path **A** authenticates purely with a QA.tech API key and never touches OAuth.
For **MR Reviews (B)**, connect with a dedicated GitLab **service user**
rather than a personal account. GitLab does not offer org-level access for
what we need, and a connection tied to a personal account breaks if that user
is removed.
This page covers both paths.
## What GitLab MR reviews do
When enabled, QA.tech can:
* detect merge request activity from your connected repository
* post an introduction or progress comment on merge requests
* run exploratory and end-to-end testing against your merge request
* publish a final results-based review comment back on the merge request
## What QA.tech Writes on GitLab MRs
QA.tech posts comments and review notes on merge requests when the integration is triggered.
| Operation | When | Description |
| :--------------------------- | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------- |
| **MR comment (in progress)** | When the agent starts testing | Comment with link to the QA.tech conversation and details (project, MR number, branch, event type). Lets you track progress. |
| **MR review note** | After tests complete | MR note with verdict (Approved, Changes Requested, or Comment), summary, test results table, and evaluation details. |
QA.tech uses your connected GitLab OAuth integration to call GitLab APIs. That
means MR comments and review updates are posted through your connected GitLab
integration identity.
Automatic MR review triggering requires native GitLab deployment and
environment signals. If your flow does not publish those signals, use the
manual or CI-assisted `@qa.tech` trigger path below.
## Set up GitLab MR reviews
Go to [Settings → Organization → Connections](https://app.qa.tech/current-project/settings/organization/connections) and connect GitLab. Connect with a dedicated GitLab **service user** rather than a personal account — GitLab does not offer org-level access for what we need, and a connection tied to a personal account breaks if that user is removed.
Open [Settings → Integrations](https://app.qa.tech/current-project/settings/integrations), select **GitLab MR Reviews**, then choose the repository you want QA.tech to review.
In the GitLab integration settings, configure:
* **Review Context** (optional instructions for reviews)
* **Auto-run on Merge Requests**
* **Include Draft Merge Requests**
* **Post Comment on Opened MR** (post an introductory QA.tech comment when an MR is opened, including waiting/progress guidance)
* **Environment Mapping** (map GitLab environments to QA.tech applications)
Saving creates or refreshes the GitLab webhook connection for your selected repository so QA.tech can process merge request, note, deployment, and pipeline events.
## Triggering a merge request review
### Option A: Automatic trigger for native GitLab deployments
If **Auto-run on Merge Requests** is enabled, QA.tech can auto-trigger reviews when deployment data is ready from native GitLab deployments and environments.
High-level behavior:
1. MR is opened or updated.
2. QA.tech checks if the MR commit is the latest and deployment(s) for that commit are ready.
3. If deployments are ready, QA.tech starts the review flow automatically.
4. If deployments are not ready, QA.tech can post a waiting comment (based on your setting) and wait for deployment readiness or a manual `@qa.tech` trigger.
### Option B: Manual trigger using a GitLab comment
Comment on the merge request and mention `@qa.tech`.
Include the preview deployment URL in the same comment. QA.tech parses URLs from MR comments and uses that URL as deployment context for the review run. In practice, this is required for reliable review targeting.
Example:
```text theme={null}
@qa.tech https://preview.example.com
```
### Option C: CI-assisted manual trigger
If your deployment process is custom, have CI post an MR comment that mentions `@qa.tech` and includes the preview URL. This gives you a consistent trigger pattern without requiring developers to comment manually each time.
### Option D: API trigger (no `@qa.tech` comment)
Trigger the same MR review directly from CI by calling the [Start change review chat API](/api-reference/chat/start-change-review-chat) with `mode: "pr"`. This runs the same review agent as Options A, B, and C and posts the same native MR review note, but you control exactly when it fires and which environment it targets, with no `@qa.tech` comment required.
This requires the repository to be connected in **Settings → Integrations → GitLab** so the agent can read the MR and post the review note.
```yaml theme={null}
qatech_mr_review:
stage: test
only:
- merge_requests
script: |
MR_URL="${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
curl -sSf -X POST "https://api.qa.tech/v1/chat/change-review" \
-H "Authorization: Bearer ${QA_TECH_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"mode\": \"pr\",
\"prUrl\": \"${MR_URL}\",
\"vcsProviderId\": \"gitlab\",
\"applicationOverrides\": [{
\"applicationShortId\": \"${QATECH_APP_SHORT_ID}\",
\"environment\": { \"url\": \"${PREVIEW_URL}\" }
}]
}"
```
The review runs asynchronously; poll [Get chat conversation](/api-reference/chat/get-chat-conversation) if you want CI to wait for the verdict. See [Start change review chat API](/api-reference/chat/start-change-review-chat) for the full request schema.
Use this same API for **post-merge** reviews when you do not have per-MR
preview environments. See [Post-merge change
reviews](#post-merge-change-reviews-no-preview-environments).
## GitLab MR review troubleshooting
**No review starts after MR open**
* Verify the repository is selected in GitLab integration settings.
* Check whether **Auto-run on Merge Requests** is enabled.
* Confirm deployment status is successful for the latest MR commit, or manually trigger with `@qa.tech`.
* If you are not using native GitLab deployment/environment signals, use the manual `@qa.tech` trigger with a preview URL.
**Draft MRs are skipped**
* Enable **Include Draft Merge Requests** if you want draft MRs reviewed.
**Manual trigger did nothing**
* Make sure the comment is on the merge request and includes `@qa.tech` (case-insensitive).
* If you expect preview-aware testing, include the preview URL in that same comment.
## API-driven GitLab CI/CD (manual test plan runs)
You can trigger QA.tech test plans from GitLab CI/CD using the REST API.
### Prerequisites
You need two values before configuring your pipeline:
* **API Token** - Your QA.tech API token
* **Test Plan Short ID** - From your test plan (e.g. `pln_abc123`)
### Configure CI/CD variables
Store your API token securely:
1. Go to **Settings → CI/CD → Variables**
2. Add variable:
* **Key**: `QA_TECH_API_TOKEN`
* **Value**: Your API token
* **Protected**: ✅
* **Masked**: ✅
## API implementation patterns
### Basic setup
```yaml theme={null}
trigger_qatech:
stage: test
variables:
QATECH_REQUEST_BODY: '{"testPlanShortId": "pln_abc123"}'
script: >
curl --request POST
--url "https://api.qa.tech/v1/run"
--header "Authorization: Bearer $QA_TECH_API_TOKEN"
--header "Content-Type: application/json"
--data "${QATECH_REQUEST_BODY}"
```
Replace `pln_abc123` with your test plan short ID (from your test plan page).
### Run test plans on merge requests
```yaml theme={null}
test_mr:
stage: test
only:
- merge_requests
variables:
QATECH_REQUEST_BODY: '{"testPlanShortId": "pln-smoke-tests_abc123"}'
script: >
curl --request POST
--url "https://api.qa.tech/v1/run"
--header "Authorization: Bearer $QA_TECH_API_TOKEN"
--header "Content-Type: application/json"
--data "${QATECH_REQUEST_BODY}"
```
### Test preview deployments via API
Pass dynamic URLs between jobs using dotenv artifacts:
```yaml theme={null}
stages:
- deploy
- test
deploy_preview:
stage: deploy
script:
- echo "PREVIEW_URL=https://preview-${CI_MERGE_REQUEST_IID}.yourdomain.com" >> deploy.env
artifacts:
reports:
dotenv: deploy.env
test_preview:
stage: test
dependencies:
- deploy_preview
before_script:
- |
echo '{"testPlanShortId":"pln-regression-suite_abc123","applications":[{"applicationShortId":"app-frontend_abc123","environment":{"url":"'$PREVIEW_URL'","name":"MR-'$CI_MERGE_REQUEST_IID'"}}]}' > /tmp/request.json
script: >
curl --request POST
--url "https://api.qa.tech/v1/run"
--header "Authorization: Bearer $QA_TECH_API_TOKEN"
--header "Content-Type: application/json"
--data @/tmp/request.json
```
You can also override device presets in the same request by adding `devicePresetShortId` to each application object in the `applications` array. For example, to test the preview deployment on a mobile device preset: `"applications":[{"applicationShortId":"app_frontend","environment":{"url":"'$PREVIEW_URL'"},"devicePresetShortId":"preset_abc123"}]`. See [Start Run API](/api-reference/runs/start-test-run) for details.
### Scheduled testing
```yaml theme={null}
nightly_tests:
stage: test
only:
- schedules
variables:
QATECH_REQUEST_BODY: '{"testPlanShortId": "pln-full-regression_abc123"}'
script: >
curl --request POST
--url "https://api.qa.tech/v1/run"
--header "Authorization: Bearer $QA_TECH_API_TOKEN"
--header "Content-Type: application/json"
--data "${QATECH_REQUEST_BODY}"
```
Set up schedules at **CI/CD → Schedules → New schedule**.
**Use GitLab schedules when:**
* Tests should run as part of your CI/CD pipeline
* You need GitLab context (branch, commit SHA)
* You want to gate deployments on scheduled test results
**Use QA.tech schedules when:**
* Tests should run independently of CI/CD infrastructure
* You prefer managing schedules in QA.tech UI
* You want to avoid consuming GitLab runner minutes
See [Test Plans](/core-concepts/test-plans#scheduled-execution) for QA.tech scheduling.
## Post-merge change reviews (no preview environments)
If you do not have preview environments for merge requests, you can run QA.tech change reviews **after** merging to your main branch. This tests the changes from each MR against your staging or production environment once they're deployed.
**How it works**
1. An MR is merged to `main`/`dev`/`staging`.
2. Your deploy job deploys to staging/production.
3. CI extracts the MR number from the merge commit message.
4. The agent fetches the MR details (diff, changed files, description).
5. Tests run against your deployed environment.
6. Results are posted back on the merged MR as a note.
```yaml theme={null}
stages:
- deploy
- review
deploy:
stage: deploy
only:
- main
script:
- echo "Your deploy steps..."
change-review:
stage: review
needs: [deploy]
only:
- main
script: |
MR_IID=$(echo "$CI_COMMIT_MESSAGE" | grep -oE '![0-9]+' | tail -1 | tr -d '!')
if [ -z "$MR_IID" ]; then
echo "No MR number found in commit message, skipping"
exit 0
fi
MR_URL="${CI_PROJECT_URL}/-/merge_requests/${MR_IID}"
curl -sSf -X POST "https://api.qa.tech/v1/chat/change-review" \
-H "Authorization: Bearer ${QA_TECH_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "{
\"mode\": \"pr\",
\"prUrl\": \"${MR_URL}\",
\"vcsProviderId\": \"gitlab\",
\"applicationOverrides\": [{
\"applicationShortId\": \"${QATECH_APP_SHORT_ID}\",
\"environment\": { \"url\": \"${STAGING_URL}\" }
}],
\"context\": \"This MR has already been merged and deployed. Test the changes and report any issues found.\"
}"
```
**Setup**
1. Add `QA_TECH_API_TOKEN`, `QATECH_APP_SHORT_ID`, and `STAGING_URL` as **CI/CD → Variables** (mark the token **Protected** and **Masked**).
2. Set `QATECH_APP_SHORT_ID` to your application's short ID (found in **Settings → Applications & Envs**, e.g. `app_gXeBl2`).
3. Set `STAGING_URL` to your staging/production URL.
4. Make sure the `change-review` job runs after your deploy job completes (`needs: [deploy]`).
5. Connect the repository in **Settings → Integrations → GitLab** so the agent can read the MR and post the review note.
**Notes**
* GitLab's default merge commit message ends with `See merge request namespace/project!123`. The script extracts the last `!` token, so it assumes the MR reference is at the end of the message.
* If you use squash merges with a custom template, include `%{reference}` in the template so the MR reference is present in the commit message.
* We extract the MR number from the commit message because `CI_MERGE_REQUEST_IID` is only set in merge request pipelines, not on push-to-`main`.
* Direct pushes to `main` without an MR are skipped automatically.
* Results are posted back on the merged MR as a note, since the MR is already merged.
No MR reference in your commits? Use `mode: "rawChanges"` instead and pass a
`changes` object with `changeDescription` and a git `diff` computed in CI. See
the [Start Change Review Chat
reference](/api-reference/chat/start-change-review-chat).
## API tips
For shared API concepts and platform-agnostic workflows, see [CI/CD Integration](/configuration/ci-cd-integration) and [Start Run API](/api-reference/runs/start-test-run). The examples below focus on GitLab-specific usage.
### Finding your test plan short ID
1. Go to [Test Plans](https://app.qa.tech/current-project/test-plans)
2. Click on a test plan
3. Check the URL: `https://app.qa.tech/.../test-plans/abc123`
4. Use the test plan short ID in API payloads (for example: `pln_abc123`)
### Building JSON payloads
For complex payloads with dynamic values, write to a file first:
```yaml theme={null}
before_script:
- |
echo '{"key": "'$VARIABLE'"}' > /tmp/request.json
script:
- curl ... --data @/tmp/request.json
```
### Custom Slack notifications
Override notification channels per-run. See [Notifications](/core-concepts/notifications#per-run-api-overrides-slack-only) for details.
### Blocking mode
Wait for test completion before proceeding with deployments:
```yaml theme={null}
trigger_qatech:
stage: test
script:
# Start run and capture shortId
- |
RESPONSE=$(curl -s -X POST \
"https://api.qa.tech/v1/run" \
-H "Authorization: Bearer $QA_TECH_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"testPlanShortId\": \"pln_abc123\"}")
SHORT_ID=$(echo "$RESPONSE" | jq -r '.run.shortId')
# Poll until completion (see Run Status API for details)
- |
while true; do
RESPONSE=$(curl -s \
"https://api.qa.tech/v1/run/$SHORT_ID" \
-H "Authorization: Bearer $QA_TECH_API_TOKEN")
STATUS=$(echo "$RESPONSE" | jq -r '.status')
if [[ "$STATUS" == "COMPLETED" || "$STATUS" == "ERROR" || "$STATUS" == "CANCELLED" ]]; then
RESULT=$(echo "$RESPONSE" | jq -r '.result')
[[ "$RESULT" == "PASSED" ]] && exit 0 || exit 1
fi
sleep 30
done
```
See [Run Status API](/api-reference/runs/get-run) for polling logic details and error handling.
## Related Documentation
* **[CI/CD Integration](/configuration/ci-cd-integration)** - Overview of integration modes
* **[GitHub App for PR Reviews](/configuration/github-app)** - GitHub version of exploratory PR reviews
* **[API Reference](/api-reference/runs/start-test-run)** - Complete API documentation
* **[Test Plans](/core-concepts/test-plans)** - Create and organize test plans
* **[Preview Environments](/core-concepts/applications-and-environments#preview-environments)** - Dynamic preview testing
# IP Access
Source: https://docs.qa.tech/configuration/ip-access-control
Configure your ingress to allow QA.tech testing traffic
## Overview
QA.tech's automated testing appears as bot traffic to CDNs and firewalls. To prevent blocking, you need to configure your CDN or firewall to allow traffic from QA.tech's IP addresses.
QA.tech uses a dedicated pool of static IP addresses (through NAT) for all test runs and agent sessions. This means you can whitelist these IPs in any security system that makes IP-based decisions.
## QA.tech IP Addresses
Find the current list of QA.tech IP addresses:
* **In the app:** [**Settings → Network**](https://app.qa.tech/current-project/settings/network)
* **Direct access:** [Get Outbound IPs API](/api-reference/infrastructure/get-outbound-ips) ([https://api.qa.tech/v1/outbound-ips](https://api.qa.tech/v1/outbound-ips))
IP addresses may change. Always use the current list from [Settings →
Network](https://app.qa.tech/current-project/settings/network) or the API
endpoint above.
**Testing a mobile app?** Mobile test traffic comes from a separate set of IP
ranges, listed under the **Mobile Testing IP Whitelist** section in [Settings
→ Network](https://app.qa.tech/current-project/settings/network). See [Mobile
App Testing](/test-features/mobile-app-testing#network-access) for details.
## What IP Whitelisting Enables
Whitelisting QA.tech's IP addresses allows your tests to:
* **Bypass CAPTCHA challenges** - Works with Google reCAPTCHA, hCaptcha, Cloudflare Turnstile, and other CAPTCHA services that use IP reputation scoring
* **Bypass rate limiting** - Avoid IP-based throttling or blocking
* **Access IP-restricted resources** - Allow QA.tech through firewall rules or IP allowlists
* **Reduce bot detection false positives** - Prevent security tools from flagging legitimate test traffic
## Cloudflare Configuration
For **Cloudflare edge blocking** (WAF / firewall) vs **Turnstile widgets in your app**, see **[Cloudflare WAF & Turnstile](/configuration/cloudflare-waf-turnstile)**.
### Recommended: AI Crawl Control
Cloudflare's [AI Crawl Control](https://developers.cloudflare.com/ai-crawl-control/) is designed for managing automated traffic like QA.tech testing.
**Setup approach:**
1. Identify QA.tech traffic in your AI Crawl Control dashboard (look for requests from QA.tech IP ranges during test execution)
2. Create allow rules for QA.tech traffic patterns
3. Monitor and adjust as needed
**Why this works best:** Automatically manages bot traffic with built-in analytics and zero configuration for most users.
### IP allowlist (WAF tools)
If you need traditional IP whitelisting:
1. Log in to your Cloudflare dashboard
2. Navigate to Security → WAF → Tools → IP Access Rules
3. Add QA.tech IP addresses to the allowlist
4. Set action to "Allow"
For detailed steps, see [Cloudflare's WAF documentation](https://developers.cloudflare.com/waf/tools/ip-access-rules/).
## AWS CloudFront Configuration
CloudFront blocking typically involves geographic restrictions, AWS WAF Bot Control, or signed URLs.
**Common solutions:**
* **AWS WAF Bot Control:** Add QA.tech IP ranges to your bot control allow rules
* **Geographic restrictions:** Ensure QA.tech's operating regions aren't blocked
* **Signed URLs/cookies:** Contact QA.tech support for integration guidance
For detailed AWS setup, see [AWS WAF Bot Control documentation](https://aws.amazon.com/waf/features/bot-control/).
## Other CDNs and Firewalls
For other CDN providers or firewall systems, add QA.tech IP addresses to your allow/whitelist rules.
The exact steps depend on your security provider, but the concept is the same across all platforms:
1. **Get QA.tech's IP addresses** from [Settings → Network](https://app.qa.tech/current-project/settings/network) or via the [Get Outbound IPs API](/api-reference/infrastructure/get-outbound-ips)
2. **Add them to your security tool's IP allowlist/whitelist**
3. **Configure the rule** to bypass security checks (CAPTCHA, rate limiting, etc.) for these IPs
**Common platforms that support IP whitelisting:**
* AWS WAF (Web Application Firewall)
* Google Cloud Armor
* Azure Front Door / Application Gateway
* Imperva / Incapsula
* Akamai
* Sucuri
* Wordfence (WordPress)
* Most enterprise firewalls and CDNs
Consult your security provider's documentation for specific instructions on IP allowlisting.
## Troubleshooting
**Still seeing 403 errors or captcha challenges?**
1. **Verify IP addresses are current** - QA.tech IPs may change; check [Settings → Network](https://app.qa.tech/current-project/settings/network)
2. **Check rate limiting rules** - QA.tech tests may trigger rate limits before allow rules apply
3. **Review geographic restrictions** - Ensure no blanket blocks on QA.tech's operating regions
4. **Wait for propagation** - CDN changes take 5-10 minutes to propagate globally
5. **Test after changes** - Run a simple test to verify your configuration
**Need help?** Contact QA.tech support with your CDN provider and error details.
# Organization Access
Source: https://docs.qa.tech/configuration/organization-access
Manage who can join your organization and how they sign in — verified domains, auto-join, and SAML SSO
This page is for **organization admins** (typically IT or security owners) who control how their team gets into QA.tech. It covers three capabilities, built around **verified email domains**:
* **Verify a domain** you own (for example, `company.com`).
* **Auto-join (JIT provisioning)** — automatically add users from a verified domain instead of inviting them one by one.
* **SAML 2.0 single sign-on (SSO)** — let your team sign in with your existing identity provider (IdP), such as Okta, Microsoft Entra ID, or Google Workspace.
All of these settings live under **Organization Settings → Authentication**, and only organization admins can manage them.
**Requirements**
* **Domain verification and auto-join:** You must be an organization **Admin or Owner**. No special plan is required.
* **SAML SSO:** In addition to admin access, **SAML SSO must be enabled for your organization by QA.tech** — it is a plan-level feature. If it is not enabled, the SAML configuration section will not appear. Contact QA.tech or your account representative to have it enabled.
See [Roles and Permissions](/core-concepts/roles-and-permissions) for more on admin access.
Domain verification and auto-join work on their own — you do **not** need SAML
SSO to use them. SAML SSO is an additional, optional layer that builds on a
verified domain.
## 1. Add and verify a domain
Verifying a domain proves you own the email domain your team uses. It is the foundation for both auto-join and SAML SSO, so start here.
Go to **Organization Settings → Authentication** and find the **Domains** section.
Enter the email domain your team signs in with — for example, `company.com`.
Public email domains (such as `gmail.com`, `outlook.com`, `yahoo.com`, and other shared providers) cannot be claimed. You must use a domain your organization owns.
After adding the domain, QA.tech shows you a verification token. Create the following **TXT** record with your DNS provider:
| Field | Value |
| --------- | --------------------------------------------------------------------------------- |
| **Host** | `_qatech-verification.` (for example, `_qatech-verification.company.com`) |
| **Type** | `TXT` |
| **Value** | The token shown in the UI, in the format `qatech-domain-verify=` |
DNS changes can take up to **48 hours** to propagate, though they are often live much sooner. If verification fails immediately after adding the record, wait and try again.
Once the DNS record is in place, return to QA.tech and click **Verify domain**. When verification succeeds, the domain shows a **Verified** badge.
## 2. User provisioning (auto-join / JIT)
Provisioning controls how users get added to your organization. There are two modes:
| Mode | Behavior |
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Invite only** *(default)* | Users must be invited before they can access the organization. |
| **Just-in-time (JIT) / auto-join** | Any user who signs in with a matching **verified** email domain is automatically added as a member on first login, and skips onboarding. |
**Auto-join does not require SAML SSO.** You can enable domain-based auto-join
for any verified domain on its own. If you later configure SSO, JIT
provisioning also applies to SSO sign-ins — users authenticating through your
IdP are added automatically on first login.
With auto-join enabled, **anyone** with an email on the verified domain who
signs in will become a member of your organization. Enable it only for domains
where every account on the domain should have access.
## 3. Configure SAML SSO for a verified domain
SAML SSO lets your team sign in through your identity provider. It builds on a verified domain and requires the SSO feature to be enabled for your organization.
**Prerequisites for this step**
* The domain is **Verified** (Step 1).
* **SAML SSO is enabled** for your organization by QA.tech.
If SAML SSO is not enabled, the SSO configuration controls will not appear.
In **Organization Settings → Authentication**, select the verified domain you want to configure and open its **SAML SSO** settings.
Supply your identity provider's SAML metadata in **one** of the following ways:
* **Metadata URL** — paste the URL your IdP publishes its metadata at (for example, `https://idp.example.com/saml/metadata`).
* **Metadata XML** — paste the raw SAML metadata XML directly.
You can usually find this metadata in your IdP's admin console under the QA.tech application's SAML or SSO settings.
Save your configuration. QA.tech provisions a SAML SSO provider bound to that domain. Once active, the domain shows an **SSO Active** badge.
**Disabling or removing SSO**
* Disabling SSO for a domain removes the SAML provider for that domain. Users on that domain will no longer sign in through your IdP.
* Removing the domain entirely also disables SSO for it.
## 4. End-user sign-in experience
Once SSO is active, your team signs in like this:
On the QA.tech sign-in screen, the user selects **Continue with SSO**.
The user enters their work email. QA.tech uses the **email domain** to find
the matching organization and redirects the user to that organization's
identity provider.
The user signs in with your IdP and is redirected back to QA.tech to
complete sign-in. If JIT provisioning is enabled, first-time users are added
to the organization automatically and skip onboarding.
If your organization enforces SSO, users on your domain are routed to the SSO
sign-in page automatically.
## Troubleshooting
**Domain won't verify**
* Confirm the TXT record host is exactly `_qatech-verification.` (for example, `_qatech-verification.company.com`) — a common mistake is omitting the `_qatech-verification.` prefix or adding the domain twice.
* Confirm the record **Type** is `TXT` and the **Value** matches the token shown in the UI exactly, including the `qatech-domain-verify=` prefix.
* DNS can take up to **48 hours** to propagate. Wait and click **Verify domain** again.
**"No SSO provider configured for this domain" at sign-in**
* The user's email domain does not have an active SAML provider. Confirm the domain is **Verified** and shows the **SSO Active** badge in **Organization Settings → Authentication**.
* Confirm the user is signing in with their **work email** on the configured domain, not a personal address.
**The SAML SSO section isn't visible**
* SAML SSO is a plan-level feature and must be enabled for your organization by QA.tech. If you don't see the SSO configuration controls, contact QA.tech or your account representative to have it enabled. (Domain verification and auto-join do not depend on this feature and are available without it.)
* Confirm you are signed in as an organization **Admin or Owner**. The app hides these settings from members.
**Need help?** Contact QA.tech support with your domain and identity provider details.
# SSH Tunnel Proxy
Source: https://docs.qa.tech/configuration/ssh-tunnel
Securely test applications behind firewalls using SSH tunnels
The SSH Tunnel Proxy feature allows you to securely test applications that are behind firewalls, in private networks, or only accessible from specific servers. By default this routes all browser testing traffic through an encrypted SSH tunnel. You can optionally limit routing to specific domains when the jump host only allows selected destinations.
## When to Use SSH Tunnel Proxy
Perfect for these scenarios:
* **Private/Internal Applications**: Test staging environments or internal tools not exposed to the internet
* **Firewall-Protected Apps**: Access applications behind corporate firewalls or VPNs
* **Security Compliance**: Maintain secure connections when testing sensitive applications
* **Development Environments**: Access local development servers or containerized applications
## How It Works
1. **SSH Connection**: QA.tech establishes a secure SSH connection to your designated server
2. **SOCKS5 Proxy**: Creates a local proxy that forwards all traffic through the SSH tunnel
3. **Browser Configuration**: Automatically configures the test browser to route traffic through the tunnel
4. **Automatic Cleanup**: Cleanly closes the tunnel when your test session ends
## Setup Instructions
Navigate to [**Settings → Network**](https://app.qa.tech/current-project/settings/network) and find the **SSH Tunnel Proxy** section. Toggle the switch to **Enable**.
Enter your SSH server details:
* **SSH Host**: The hostname or IP address of your SSH server (e.g., `jump-server.company.com`)
* **SSH User**: The username for SSH authentication (e.g., `ubuntu`, `deploy`)
* **SSH Port**: Custom SSH port (optional, default: 22)
* **Only Route These Domains** (Advanced): Optional host patterns such as `*.preview.example.com`. When set, only matching hosts go through the tunnel; Auth0, CDNs, and other public APIs go direct. Leave empty to route all traffic. Matching is enforced in the local SOCKS5 server opened by the tunnel (non-matching hosts connect DIRECT from the agent).
Click **Generate SSH Key** to create a secure key pair. Copy the generated **public key** and add it to your server's `~/.ssh/authorized_keys` file:
```bash theme={null}
# On your server, add the public key with security restrictions:
echo 'restrict,port-forwarding,command="/bin/false" ssh-rsa AAAAB3NzaC1yc2E... your-generated-key' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
```
The `restrict,port-forwarding,command="/bin/false"` prefix is **highly recommended** for security. This prevents QA.tech from getting shell access to your server while still allowing the necessary port forwarding for testing.
Click **Test Connection** to verify everything is working, then **Save Configuration** to store your settings securely.
## Server Requirements
Your SSH server needs to support:
* **SSH Protocol 2** (standard on modern systems)
* **Public key authentication**
* **Dynamic port forwarding** (SOCKS proxy support)
Most Linux servers (Ubuntu, CentOS, Amazon Linux) support this out of the box.
## Recommended Architecture
### SSH Jump Server Setup
**Best Practice Architecture:**
1. **SSH Jump Server**: Deploy a dedicated server with a public IP outside your private network
2. **IP Whitelist**: Configure firewall rules to only allow SSH connections from QA.tech IPs
3. **Internal Access**: Allow the jump server to access only the specific web applications needed for testing
4. **Network Segmentation**: Keep your applications in a private network, accessible only via the jump server
**Benefits:**
* ✅ **Minimal Attack Surface**: Only SSH port exposed to internet
* ✅ **Controlled Access**: Jump server only accesses specific applications
* ✅ **Audit Trail**: All connections flow through a single, monitored entry point
### Architecture Diagram
```mermaid theme={null}
graph TB
subgraph "QA.tech Infrastructure"
TestRunner["🧪 Test Runner"]
subgraph "Isolated Browser Workers"
Worker1["🖥️ Browser Worker 1 Test Session A"]
Worker2["🖥️ Browser Worker 2 Test Session B"]
Worker3["🖥️ Browser Worker 3 Test Session C"]
Worker4["🖥️ Browser Worker 4 Test Session D"]
WorkerN["🖥️ Browser Worker N Test Session N (up to 10+ parallel)"]
end
TestRunner --> Worker1
TestRunner --> Worker2
TestRunner --> Worker3
TestRunner --> Worker4
TestRunner --> WorkerN
end
subgraph "Customer Infrastructure"
BastionHost["🔐 Bastion Host (SSH Jump Server) Public IP with Firewall"]
subgraph "Private Network"
WebApp1["🌐 Web Application 10.0.1.100:80/443"]
WebApp2["🌐 API Server 10.0.1.200:80/443"]
Database["🗄️ Database 10.0.1.50:5432"]
end
end
Worker1 -.->|"SSH Tunnel Encrypted Connection"| BastionHost
Worker2 -.->|"SSH Tunnel Encrypted Connection"| BastionHost
Worker3 -.->|"SSH Tunnel Encrypted Connection"| BastionHost
Worker4 -.->|"SSH Tunnel Encrypted Connection"| BastionHost
WorkerN -.->|"SSH Tunnel Encrypted Connection"| BastionHost
BastionHost -->|"HTTP/HTTPS Port 80/443"| WebApp1
BastionHost -->|"HTTP/HTTPS Port 80/443"| WebApp2
WebApp1 --> Database
WebApp2 --> Database
classDef qatech fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef customer fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
classDef secure fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
classDef private fill:#fff3e0,stroke:#e65100,stroke-width:2px
class TestRunner,Worker1,Worker2,Worker3,Worker4,WorkerN qatech
class BastionHost secure
class WebApp1,WebApp2,Database private
```
**Key Architecture Points:**
* **Isolated Workers**: Each browser session runs on a dedicated, isolated worker
* **Per-Session Tunnels**: Fresh SSH connection created for every test
* **Customer Separation**: Workers never handle multiple customers simultaneously
* **Encrypted Transport**: All traffic flows through encrypted SSH tunnels
* **Parallel Testing**: Up to 10+ concurrent browser workers can connect simultaneously
### Whitelist QA.tech IPs for SSH access
Get the current QA.tech IP addresses from [**Settings → Network → IP
Whitelist**](https://app.qa.tech/current-project/settings/network) in your
project dashboard.
## Security Features
### SSH Key Security
**Recommended SSH Key Restrictions:**
When adding the QA.tech public key to your `~/.ssh/authorized_keys` file, always include these security restrictions:
```bash theme={null}
restrict,port-forwarding,command="/bin/false" ssh-rsa AAAAB3NzaC1yc2E... your-qa-tech-key
```
**What these restrictions do:**
* `restrict` - Disables all forwarding and other features by default
* `port-forwarding` - Explicitly allows only port forwarding (required for SOCKS proxy)
* `command="/bin/false"` - Prevents shell access, immediately exits if someone tries to get a shell
**Why this matters:** Without these restrictions, the SSH key could potentially be used to gain shell access to your server. These restrictions ensure QA.tech can only create the tunnel connections needed for testing.
### Additional Security Layers
* **Encrypted Key Storage**: Private keys are encrypted and stored securely
* **Isolated Workers**: Each browser session runs on a dedicated worker
* **Per-Session SSH Connections**: A fresh SSH tunnel is created for each browser session
* **No Cross-Customer Data**: Worker isolation prevents any data leakage between customers
* **Automatic Cleanup**: SSH connections and workers are terminated after each test session
* **Limited Scope**: SSH keys only allow port forwarding, no shell or file access
## Parallel Testing Configuration
If you plan to run multiple tests simultaneously, configure your SSH server to handle concurrent connections:
```bash theme={null}
# In /etc/ssh/sshd_config
MaxSessions 50 # Allow up to 50 concurrent sessions per connection
MaxStartups 30:30:60 # Allow up to 30 pending connections
```
## Troubleshooting
### Connection Failed
* Check SSH server is running: `sudo systemctl status sshd`
* Verify firewall rules allow SSH connections
* Test manual connection: `ssh user@host` from your local machine
### Authentication Failed
* Verify public key was added to `~/.ssh/authorized_keys`
* Check file permissions: `chmod 600 ~/.ssh/authorized_keys`
* Try regenerating keys in the QA.tech interface
### Public APIs Time Out Through the Tunnel
If login or third-party calls (Auth0, Intercom, CDNs) time out while your app loads, the jump host may only allow traffic to specific destinations. Under **Advanced Settings**, set **Only Route These Domains** to those hosts (e.g. `*.preview.example.com`) so everything else goes direct.
### Connection Timeout
* Ensure the SSH host is reachable from QA.tech servers
* Verify QA.tech IPs are whitelisted in your firewall (see [IP Access Control](/configuration/ip-access-control))
* Check if SSH daemon allows connections from external IPs
* Use **Test Connection** — it runs from the same browser-agent network as real tests and shows the outgoing IP your bastion will see
The connection test dials SSH from a browser agent (Cloud NAT), not from the
app UI servers. The result panel shows the outgoing IP so you can confirm it
matches your allowlist.
### Parallel Connection Issues
* Check your server's SSH configuration for `MaxSessions` and `MaxStartups`
* Monitor server resources (CPU, memory, network) during parallel tests
* Start with fewer parallel connections and scale gradually
## Technical Details
* **Protocol**: SSH-2 with SOCKS5 dynamic port forwarding
* **Encryption**: RSA 2048-bit or Ed25519 key pairs (automatically generated)
* **Timeout**: 20-second SSH connection timeout for reliability
* **Keep-Alive**: Automatic connection maintenance during test sessions
The SSH Tunnel Proxy feature is currently in Beta. If you encounter any issues
or have feature requests, please contact our support team.
# Vercel Firewall
Source: https://docs.qa.tech/configuration/vercel-firewall
Allow QATechBot traffic through the Vercel Firewall with a custom bypass rule
When your app is hosted on Vercel, the **Vercel Firewall** (WAF) can block QA.tech before your application runs. QA.tech identifies as [**QATechBot**](/bot) in the `User-Agent` header—you can add a firewall rule to **bypass** security checks for that traffic.
This is separate from **[Vercel Preview
Protection](/configuration/vercel-preview-protection)** (password protection,
Vercel Authentication, deployment protection). Use this page for **Firewall /
WAF** rules; use the preview guide for protected preview URLs.
## Allow QATechBot with a custom rule
Create a custom firewall rule in your Vercel project that bypasses remaining rules when the User-Agent contains `QATechBot`:
In the [Vercel dashboard](https://vercel.com), open your project → **Firewall** (or **Security → Firewall**).
Create a new custom rule with these settings:
| Field | Value |
| -------------------------- | -------------------------------------------------------------------------------- |
| **Name** | `Allow QATechBot traffic` |
| **Description** (optional) | `Bypass security rules for QATechBot user agent to allow monitoring and testing` |
| **If** | **User Agent** → **Contains** → `QATechBot` |
| **Then** | **Bypass** |
Save and publish the rule. Vercel applies firewall changes globally within seconds—no redeploy required.
The **Bypass** action lets matching requests skip subsequent custom firewall rules (challenge, deny, rate limit, etc.) so QA.tech can load pages and run tests.
**User-Agent can be spoofed.** Any client can send `User-Agent:
...QATechBot...`. For stronger assurance, combine this rule with [QA.tech IP
allowlisting](/configuration/ip-access-control) or validate traffic using [bot
verification](/bot#verifying-qatechbot-traffic).
## User-Agent reference
QA.tech appends this suffix to browser automation traffic:
```http theme={null}
QATechBot/1.0 (+https://docs.qa.tech/bot)
```
Your rule should match on `QATechBot` (substring), which covers the full automation User-Agent.
## CLI example
You can also add the rule with the [Vercel CLI](https://vercel.com/docs/cli/firewall):
```bash theme={null}
vercel firewall rules add \
--action bypass \
--condition "user_agent,sub,QATechBot" \
--name "Allow QATechBot traffic" \
--description "Bypass security rules for QATechBot user agent to allow monitoring and testing"
```
Publish staged firewall changes when prompted.
## Related
Bypass deployment protection on preview URLs
User-Agent format and traffic verification
Allowlist QA.tech egress IPs
Edge blocking and Turnstile on Cloudflare
**External:** [Vercel WAF custom rules](https://vercel.com/docs/vercel-firewall/vercel-waf/custom-rules), [Vercel Firewall CLI](https://vercel.com/docs/cli/firewall)
# Vercel Preview
Source: https://docs.qa.tech/configuration/vercel-preview-protection
Configure QA.tech to bypass Vercel deployment protection for automated testing
# Vercel Preview Protection Bypass
When testing Vercel preview deployments that are protected with Password Protection, Vercel Authentication, or Trusted IPs, you need to configure QA.tech to bypass these protection mechanisms. This guide shows you how to set up automated testing with Vercel's Protection Bypass for Automation feature.
This guide is for testing protected Vercel preview deployments from CI/CD
pipelines. See [CI/CD Integration](/configuration/ci-cd-integration) for an
overview of integrating QA.tech with CI/CD systems. For **Vercel Firewall /
WAF** rules, see [Vercel Firewall](/configuration/vercel-firewall).
## Overview
Vercel's Protection Bypass for Automation allows automated tools like QA.tech to access protected deployments using a special secret. This bypasses all deployment protection methods including:
* Password Protection
* Vercel Authentication
* Trusted IP restrictions
## Setting Up Vercel Protection Bypass
### Step 1: Enable Protection Bypass in Vercel
Go to your Vercel project dashboard and navigate to **Settings → Deployment
Protection**
Find the **Protection Bypass for Automation** section and enable it. This
will generate a secret token.
Copy the generated secret - you'll need this for configuring QA.tech
The secret is automatically added to your Vercel deployments as the
environment variable `VERCEL_AUTOMATION_BYPASS_SECRET`. Regenerating the
secret will invalidate previous deployments, requiring a redeploy to use the
new value.
### Step 2: Configure Project URL with Query Parameters
Configure your project URL to include the bypass parameters directly:
Go to [**Settings → Project
Settings**](https://app.qa.tech/current-project/settings) in your
QA.tech project
Modify your project URL to include the bypass query parameters. For example,
if your Vercel preview URL is:
`https://example-vercel-protected-git-branch-qa-tech.vercel.app`
Update it to:
`https://example-vercel-protected-git-branch-qa-tech.vercel.app?x-vercel-protection-bypass=YOUR_SECRET&x-vercel-set-bypass-cookie=true`
Replace `YOUR_SECRET` with the secret you copied from Step 1.
Save your project settings. QA.tech will now use this URL format for all
tests, automatically bypassing Vercel's protection.
When you visit a Vercel URL with these query parameters, Vercel automatically
redirects to a clean URL without the query parameters after setting the bypass
cookie. This makes it a clean solution that doesn't require managing custom
headers.
## Query Parameter Configuration Details
### Required Parameters
* **Parameter**: `x-vercel-protection-bypass`
* **Value**: Your generated Vercel secret
* **Purpose**: Bypasses all deployment protection mechanisms
* **Parameter**: `x-vercel-set-bypass-cookie`
* **Value**: `true`
* **Purpose**: Sets a bypass cookie for subsequent requests, ensuring consistent access throughout the test session
# Agent Cache
Source: https://docs.qa.tech/core-concepts/agent-cache
Understand how Agent Cache speeds up your tests and when to disable it
Agent Cache is **enabled by default** for all tests. It stores AI reasoning from successful test runs and reuses those decisions when your pages haven't changed - making subsequent runs faster.
## Why It's On by Default
Caching makes your tests **faster**. Each AI reasoning step takes 2-5 seconds - with caching enabled, those decisions are reused instantly when your pages haven't changed.
For stable pages - admin dashboards, settings screens, checkout flows - caching delivers significant speed improvements with no trade-offs.
## How It Works
When a test runs, the AI analyzes each page, decides what action to take, and executes it. With caching enabled, these reasoning decisions are stored and reused on subsequent runs:
```
Without Cache With Cache
───────────────── ─────────────────
1. Analyze page 1. Check cache
2. AI decides action → 2. Cache hit? Use stored decision
3. Execute action 3. Execute action
4. Repeat... 4. Repeat...
```
**What gets cached:** AI reasoning decisions - which element to click, what text to type, how to navigate.
**What still executes:** All browser actions (clicks, typing), screenshots, page loads, and verification steps run normally every time.
**Cache invalidation:** The system automatically detects when page content changes significantly and fetches fresh AI decisions. Failed test runs never save to cache, preventing "bad" reasoning patterns from persisting.
## When to Disable Agent Cache
Most users never need to disable caching. Consider turning it off only when
debugging flaky tests or testing brand-new features where you want fresh AI
analysis every run.
## How to Disable Agent Cache
Navigate to **Test Cases**, select a test, and click **Edit**.
In the sidebar, click the **Settings** tab (not "Steps").
Scroll down to locate the **Agent Cache (BETA)** section.
Uncheck **Enable caching for this test** and save your changes.
## Limitations
Agent Cache is in BETA. While cache entries automatically invalidate when page
content changes significantly, some minor UI updates may be missed.
* **Page content affects hit rate** - Cache keys include visible page content, so pages with dynamic elements will naturally see lower hit rates
* **30-day expiration** - Unused cache entries expire after 30 days
* **Only successful runs cache** - Failed tests never store decisions
## Related Documentation
* [Creating Tests](/best-practices/creating-tests) - Test creation and agent settings
* [AI Agent Testing](/core-concepts/ai-agent-testing) - How the AI agent works
# AI Agents
Source: https://docs.qa.tech/core-concepts/ai-agent-testing
Autonomous AI testing
QA.tech uses specialized AI agents to test your application like humans would, but faster and more thoroughly. Our system automatically routes testing tasks to the right agent based on context-no manual configuration needed.
## Our AI Agent System
| Entry Point | Purpose | How It Activates | Learn More |
| :----------------------- | :------------------------------------------------ | :------------------------------- | :---------------------------------------------------- |
| **Chat Assistant** | Interactive testing, test creation, site analysis | You start a conversation | [AI Chat Assistant](/core-concepts/ai-chat-assistant) |
| **PR Review** | Autonomous testing of every pull request | Automatic on PR open/update | [GitHub App](/configuration/github-app) |
| **On-Demand PR Testing** | Deep testing with custom instructions | `@qatech` mention in PR comments | [GitHub Actions](/configuration/github-actions) |
## How It Works in Practice
Here's what happens when you open a PR that changes your checkout flow:
**1. Automatic Detection**
```
PR opened: "Add Apple Pay to checkout"
→ PR Review agent activates
```
**2. Change Classification**
```
Analyzing diff...
├─ checkout.tsx: USER-FACING ✓
├─ payment-methods.ts: USER-FACING ✓
└─ README.md: DOCS ONLY ✗
Classification: USER-FACING changes detected
```
**3. Coverage Assessment**
```
Existing tests found:
├─ "Complete checkout with credit card" ✓
├─ "Complete checkout with PayPal" ✓
└─ Apple Pay integration: NOT COVERED ⚠️
```
**4. Test Generation**
```
Creating new test:
"Complete checkout flow with Apple Pay"
├─ Add item to cart
├─ Navigate to checkout
├─ Select Apple Pay as payment method
├─ Verify payment confirmation
└─ Verify order appears in account
```
**5. Execution & Review**
```
Running against PR preview environment...
✅ All tests passing (3/3)
Posting GitHub review:
"✅ Tests passing - Apple Pay integration verified"
```
This entire workflow runs autonomously - no human intervention required.
## When to Use AI Agents vs Scripts
| Scenario | AI Agents | Scripts | Manual QA |
| :----------------------------------- | :----------------------------------------------------------------------------- | :---------------------------------- | :------------------------------------ |
| **Exploratory testing** | ✅ Best - discovers edge cases through varied behavior | ❌ Fixed paths only | ✅ Good - but slow and expensive |
| **Regression testing** | ✅ Good - handles UI changes gracefully | ✅ Best - fastest execution | ❌ Too repetitive |
| **Exact same steps every time** | ⚠️ Possible but overkill | ✅ Best - fully deterministic | ❌ Error-prone over time |
| **Testing multiple user flows** | ✅ Best - tries different approaches automatically | ⚠️ Need separate script per variant | ✅ Good - but doesn't scale |
| **Complex calculations** | ❌ Use assertions in code instead | ✅ Best - precise math | ⚠️ Manual calculation prone to errors |
| **Form filling with realistic data** | ✅ Best - understands context | ⚠️ Need hardcoded test data | ✅ Good - but tedious |
| **Testing preview environments** | ✅ Best - [automatic PR reviews](/configuration/github-app) | ⚠️ Need CI/CD integration | ❌ Requires manual coordination |
| **First-time test coverage** | ✅ Best - [AI Chat Assistant](/core-concepts/ai-chat-assistant) sets up quickly | ❌ Requires upfront investment | ⚠️ Slow to establish baseline |
In practice, most teams use a combination: AI agents for exploration and edge cases, scripts for critical deterministic flows, and manual QA for subjective evaluation (design, UX, brand consistency).
## How It All Connects
The system coordinates automatically based on what you're trying to accomplish:
**Creating tests via Chat**
```
You: "Create 5 tests for the checkout flow"
→ Analyzes your application structure
→ Creates tests prioritizing revenue-critical paths
→ Shows suggestions for your approval
```
**PR Review fills coverage gaps**
```
PR detected with untested functionality
→ Creates tests for new features
→ Runs all relevant tests
→ Posts review with results
```
You don't manage this coordination - it happens automatically.
## Test Execution Model
QA.tech uses Claude Haiku 4.5 as the default AI model for test execution. This model provides the fastest test execution while maintaining high-quality results.
| Model | Speed | Best For |
| :----------------------------- | :------- | :---------------------------------------------- |
| **Claude Haiku 4.5** (default) | Fastest | Most tests - recommended for day-to-day testing |
| Claude Sonnet 4.5 | Moderate | Complex scenarios requiring deeper reasoning |
The AI model handles all test execution decisions: navigating your application, filling forms, clicking buttons, and verifying outcomes. You write test goals in natural language, and the model figures out how to achieve them.
You can override the agent per-test in the Advanced settings when creating or
editing a test. See [Creating
Tests](/best-practices/creating-tests#choosing-an-ai-agent) for details.
## Get Started
Write tests in natural language. The AI generates steps automatically.
Conversational interface to create, edit, and manage tests.
Install the GitHub App for autonomous reviews on every PR.
Trigger tests from your deployment pipeline.
# AI Chat Assistant
Source: https://docs.qa.tech/core-concepts/ai-chat-assistant
Natural language interface for managing your QA.tech projects
The AI Chat Assistant is your intelligent testing companion, built directly into the QA.tech platform. It uses natural language processing to help you manage tests, analyze your application, and get QA guidance without clicking through menus.
You can access the chat assistant by clicking the chat icon in your project dashboard. Just describe what you want to do in plain English, and the assistant will use its 20+ specialized tools to help you accomplish your goal.
## Quick Start Examples
| What you want | Ask this | What happens |
| ------------------------------- | ------------------------------------------------------- | ----------------------------------------------------------------- |
| Create tests for a feature | "Generate 5 tests for the checkout flow" | Shows suggestions you can review, edit, and add |
| Run tests | "Run the login test" | Starts in background, notifies when complete |
| Edit a test | "Add a logout step to the checkout test" | Shows visual diff for approval, then validates |
| Use existing tests as templates | "Create tests for returns similar to my checkout tests" | Analyzes your checkout tests' structure and applies the pattern |
| Crawl behind authentication | "Crawl admin panel from the Login test" | Uses login test's session - no re-authentication needed |
| Bulk edit multiple tests | "Add logout step to all checkout tests" | Edits multiple tests at once, shows individual diffs to review |
| Skip repetitive setup | "Make this test resume from Login" | Inherits browser state, cookies, and session from another test |
| Search tracker issues | "Find Linear issues about checkout" | Searches your connected Linear or Jira for matching issues |
| Create a tracker issue | "Create a Jira ticket for the broken nav menu" | Creates a new issue in your connected Linear team or Jira project |
## Core Workflows
### Creating Tests
Ask the assistant to generate tests for specific flows or features. It will suggest test cases that you can review, edit, or accept.
> "Generate 5 tests for the checkout flow"
> "Create a test that validates user registration with email verification"
You can also provide the assistant with a set of test cases (as text, a document, or by referencing existing tests) and have it create new tests or update current ones based on those examples.
> "Create tests for the returns flow similar to my checkout tests"
> "Here are my test cases: \[paste list]. Generate tests based on these patterns or update current ones"
The assistant analyzes your application's [knowledge graph](/core-concepts/knowledge-graph) and any examples you provide to suggest relevant tests. Tests appear as interactive suggestions - click "Add Selected Tests" to commit them to your project.
### Running Tests
Ask the assistant to run tests and you'll be notified when they complete.
> "Run the login test"
> "Execute all checkout tests against the staging environment"
**What happens:** The assistant validates your test cases, starts execution in the background, and you'll see results when the run completes. Test runs typically take a few minutes depending on complexity.
### Accessing Test Run Results
The assistant can access detailed step-by-step results for test runs, but with an important limitation:
**What the assistant CAN access:**
* Detailed step-by-step results (screenshots, actions, step descriptions) for test runs it triggered in the current chat conversation
* Action logs showing what happened during test execution
* Visual evidence of test steps and outcomes
**What the assistant CANNOT access yet:**
* Runs triggered from the UI dashboard
* Runs triggered via API
* Scheduled test plan runs
* Runs from previous chat conversations
* Runs by run ID or test case ID (only runs linked to the current conversation)
**How it works:** When you ask the assistant to run tests in chat, it creates a link between the chat conversation and the test run. After the run completes, you can ask questions like "What happened in that test run?" or "Show me screenshots from the failed steps" and the assistant will retrieve detailed action logs and screenshots from that specific run.
If you need to analyze runs triggered outside of chat (from the UI, API, or scheduled plans), use the [Test Results dashboard](/core-concepts/tests-and-results) instead.
### Editing Existing Tests
Describe changes to tests in natural language. The assistant shows a visual diff for you to review before applying.
> "Change step 3 to wait for the spinner first"
> "Add verification that the success message appears"
> "Make this test resume from my Login test"
After you approve changes, the test runs automatically to validate the updates. See [Creating Tests](/best-practices/creating-tests#refine-tests-via-chat) for more editing examples.
### Analyzing Your Application
Ask the assistant to crawl and analyze your site to expand its knowledge of your application's features.
> "Crawl my admin panel"
> "Analyze the checkout flow to find features I should test"
The assistant can start from a specific URL or resume from a test's output state (useful for authenticated areas). Results include screenshots and discovered interactions. See [Crawling Sessions](/core-concepts/crawling) for configuration details.
## Make the Assistant More Effective
The assistant understands context and can handle follow-up questions, so you can have natural back-and-forth conversations about your testing needs. It relies on [Crawling](/core-concepts/crawling), [tests](/core-concepts/tests-and-results), and Knowledge for context gathering.
**Add Knowledge to improve test creation**: The Chat Assistant works best when it understands your application. Add documentation URLs, project context, and domain-specific information to your [Knowledge Base](/core-concepts/knowledge) so the assistant can provide more accurate test suggestions and better understand your unique workflows.
The assistant automatically searches your knowledge base and [knowledge graph](/core-concepts/knowledge-graph) when generating tests and answering questions, giving you context-aware responses tailored to your specific application.
**Note:** Knowledge and the knowledge graph are used when creating tests through chat, but not during test execution. When tests run, the agent only uses the test's goal, steps, and a targeted subset of knowledge - [Agent Rules](/core-concepts/knowledge#rules), Agent Visuals, the Project Summary, and any knowledge items attached to the test - to navigate and verify results.
## Uploading Files and Documents
You can upload files directly into chat to give the assistant additional context for test creation.
**Supported Formats:**
| Format | How It's Processed |
| --------------------------------- | ------------------------------------------------- |
| **PDF** | Text and images are both extracted and understood |
| **Images** (PNG, JPEG, GIF, WebP) | Analyzed visually by the AI |
| **Text files** | Content is read and used as context |
**PDF Processing:**
When you upload a PDF, the AI reads both the text content and any images, diagrams, or screenshots within the document. This means you can upload:
* Product specifications with annotated screenshots
* Design documents with wireframes
* Requirements documents with flowcharts
* Bug reports with visual evidence
The AI understands the full content, not just the text.
**Context Persistence:**
* Files remain available throughout your chat conversation
* You can reference uploaded content in follow-up messages
* Very long conversations may compress older file content to stay within limits, but recent uploads are always fully available
**When to Upload to Chat vs Project Knowledge:**
| Upload to Chat | Add to Project Knowledge |
| ------------------- | ------------------------ |
| Experimental specs | Official documentation |
| PR descriptions | Stable requirements |
| One-time references | Compliance guidelines |
| Work in progress | Permanent product rules |
Files uploaded in chat are isolated to that conversation - other team members cannot see them. For shared documentation, use [Project Knowledge](/core-concepts/knowledge) instead.
## What Can You Do with the Chat Assistant
**Test Management**
The assistant can list your existing [tests](/core-concepts/tests-and-results), generate new test cases based on your requirements, run tests in the background, and fetch detailed information about specific tests including their steps, [configurations](/core-concepts/configs), and [dependencies](/core-concepts/dependencies). It understands test scenarios (grouping folders) and [test plans](/core-concepts/test-plans), making it easy to organize and trigger your test suite. You can also edit existing tests through conversation - describe changes, review a visual diff, and apply with one click.
**Application Analysis**
It can crawl and analyze your website to discover features, search through the [knowledge graph](/core-concepts/knowledge-graph) to find specific functionality from previous crawls, and search your project's [knowledge base](/core-concepts/knowledge). This helps the assistant provide context-aware suggestions tailored to your application.
**Configuration Management**
The assistant can show you what [configurations](/core-concepts/configs) are available in your project, help you create new configurations for credentials and test data, and display interactive forms right in the chat to collect configuration details. It supports config types like username/password, username/password with 2FA, file uploads, and API calls.
**Issue Tracking**
If you have [Linear](/integrations/linear) or [Jira](/integrations/jira) connected, the assistant can search for issues by keyword, fetch a specific issue by key (e.g. `PROJ-42`), list recent issues, and create new ones in your configured team or project. For Jira, it reads title, description, status, assignee, priority, issue type, and labels from your configured project — see [Jira integration](/integrations/jira) for what is and is not read. Jira users can also filter by issue type such as Epic, Bug, Story, or Task. If both integrations are connected, mention which tracker you want (e.g. "search **Jira**"); otherwise the assistant defaults to Linear.
**Help & Support**
The assistant can search QA.tech's documentation to answer questions about platform features and capabilities, and provide you with contact information for customer support when you need help beyond what the AI can provide.
**Safe Experimentation**
Tests generated in chat aren't committed until you explicitly click "Add Selected Tests" - you can review, edit, or reject them without affecting your project. Files uploaded in chat are isolated to that conversation only, so you can experiment freely without impacting teammates. See [How Knowledge Works](/core-concepts/knowledge#how-knowledge-works) for details on chat isolation and when to move knowledge to your project.
## How the Assistant Adapts
The Chat Assistant automatically adjusts its approach based on what you're trying to accomplish:
| When you ask to... | The assistant will... |
| ----------------------- | ----------------------------------------------------------------------------------------------- |
| Create or suggest tests | Prioritize revenue-critical flows first (checkout, payments, bookings), then secondary features |
| Run tests | Validate test cases, start execution in background, notify you when complete |
| Analyze your site | Crawl pages, discover features, build understanding of your application |
| Edit existing tests | Show visual diffs for approval, then run tests to validate changes |
You don't need to specify how - just describe what you want naturally and the assistant handles the details.
## What the Assistant Can Access
The assistant has access to your project's data and can perform actions on your behalf:
| Capability | What it can do |
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
| **Tests** | List, inspect, create, edit, and run your test cases |
| **Site Analysis** | Crawl pages, discover features, search what it learned |
| **Configurations** | View existing configs, create new ones, show forms for credential input |
| **Knowledge** | Search your project's knowledge base and QA.tech documentation |
| **Project Info** | Access applications, environments, device presets, test plans |
| **Issue Trackers** | Search, fetch, and create issues in connected [Linear](/integrations/linear) or [Jira](/integrations/jira) |
| **Support** | Connect you with customer support when needed |
**PR testing** is not available through chat. For automatic PR reviews and
testing, use the [GitHub App](/configuration/github-app) or [GitHub
Actions](/configuration/github-actions).
# Projects, Applications, Environments
Source: https://docs.qa.tech/core-concepts/applications-and-environments
Organize your testing infrastructure with Applications and Environments, and test dynamic preview deployments
QA.tech uses a hierarchical structure of **projects**, **applications**, and **environments** to organize your testing infrastructure. Understanding when to use each level helps you maximize test reuse and build efficient CI/CD workflows.
## Understanding the Hierarchy
```
Organizations (billing, team management)
└── Projects (team access, complete isolation)
└── Applications (distinct apps with own test suites)
└── Environments (same app, different deployments)
```
### Organizations
**Organizations** are the top-level container for your company or team. They contain multiple projects and manage billing and team access at the organization level.
### Projects
**Projects** are containers for team access and complete isolation between product lines.
Use separate projects only when there are no possible shared user flows between the things being tested. For example:
* Completely separate product lines with no shared functionality
* Different teams that need complete isolation
* A consultant working with completely separate clients
### Applications
**Applications** represent distinct apps or services you want to test. **Test cases belong to applications** - each test is associated with one application.
Use separate applications when they have their own set of tests that won't run in other applications. For example:
* Customer-facing app vs admin panel (different test suites)
* Web app vs mobile app (different user flows)
* Frontend vs backoffice (different functionality to test)
### Environments
**Environments** represent different configurations of the same application - typically different URLs for the same app.
Use environments when you want to run the same tests against the same application in different stages or markets. For example:
* Same app, different deployments (dev, staging, production)
* Same app, different markets (/en, /de, /se)
* Same app, different preview deployments (PR branches)
**Key benefit:** Tests written for an application can run on any of its environments without modification.
### Maximum Concurrent Tests
Each environment can limit how many test cases run in parallel against that environment's URL. Leaving **Maximum Concurrent Tests** empty allows unlimited parallel runs for that environment. A positive number limits parallel tests to that value, with additional tests waiting in queue.
When a run uses multiple environments, the **lowest** configured limit applies.
See [Parallel Test Execution and Concurrency Limits](/core-concepts/parallel-test-execution) for how to configure this in the dashboard and when to use limits.
### Production Environment Toggle
When creating or editing an environment, you can mark it as a production environment using the "Production Environment" toggle. Currently, this setting is used for data collection to understand how many customers run tests against production environments. In the future, we may add additional safety checks to help prevent breaking production environments (such as avoiding overload or more intensive security testing).
## Common Patterns
### Complete Hierarchy Example
Here's a comprehensive example showing the full hierarchy:
**Organization: Pet Solutions Ltd**
```
Project: Veterinary App
├── Applications:
│ ├── Customer Web App
│ ├── Veterinarian Web App
│ ├── Veterinarian Mobile App
│ └── Backoffice Web App
└── Environments: Dev, Staging, Acceptance, Production
Project: Netflix for Pets
├── Applications:
│ ├── Web Video Player
│ ├── Admin App
│ └── Mobile Video Player
└── Environments: Dev, Staging, Production
```
**Why separate projects:** Veterinary App and Netflix for Pets have no shared user flows - they're completely separate products. Different products within the same team should typically be different Applications, not Projects. Only create separate Projects when there's no possible shared user flows (like a consultant working with isolated clients).
### Staging vs Production
**Use case:** Test the same functionality against staging and production environments.
**Structure:** One application with multiple environments.
**Example:**
```
Project: E-commerce Platform
└── Application: Storefront
├── Environment: Staging (staging.store.com)
└── Environment: Production (store.com)
```
**Benefits:** Write tests once, run against both environments. Use test plans to select which environment to test.
### Multi-Market/Storefronts
**Use case:** Test functionality across different markets or storefronts.
**Decision factor:** Will the same tests run in both markets? If yes, use environments. If the markets have different features or user flows requiring different test suites, use separate applications.
**Example with environments (same tests, different markets):**
```
Project: Global Store
└── Application: Storefront
├── Environment: Sweden (store.com/se)
├── Environment: Germany (store.com/de)
└── Environment: US (store.com/us)
```
Same checkout flow, same product catalog - just different markets. The same tests run in all environments.
**Example with applications (different test suites):**
```
Project: Multi-Brand Platform
├── Application: Germany Storefront
└── Application: Sweden Storefront
```
Different brands with different features, product catalogs, or checkout flows. Each requires its own set of tests.
### Multi-App Flows
**Use case:** Test flows that span multiple applications (e.g., create order in frontend, verify in admin panel).
**Structure:** Multiple applications with test dependencies.
**Example:**
```
Project: E-commerce Platform
├── Application: Customer Frontend
│ └── Environment: Production
└── Application: Admin Panel
└── Environment: Production
```
**Important:** Browser state (sessions, cookies) doesn't transfer across different domains. When testing across applications with different domains (e.g., `customer.example.com` and `admin.example.com`), use [Wait For dependencies](/core-concepts/dependencies#application-environment-and-domain-constraints) to pass data between tests. Resume From dependencies can work across applications, but only if they're in the same Environment and share the same domain.
### Third-Party Integrations
**Use case:** Test end-to-end flows that require interacting with both your application and a third-party service's web interface in the same test flow.
**When to use this pattern:** Some integrations require your tests to interact with external services that have their own web UIs. For example:
* **OAuth flows:** Your app redirects to Google/Microsoft for authentication, then redirects back
* **Payment providers:** Checkout redirects to Stripe/PayPal payment pages, then returns to your app
* **Admin panels:** You need to configure settings in a third-party admin panel (e.g., HubSpot, Salesforce) before testing your app's integration
**API Calls vs Separate Applications:** If you only need to make HTTP requests
to third-party APIs (no UI interaction), use [API Call
configs](/test-features/api-calls) instead of creating separate applications.
API Call configs let you fetch data, authenticate via API, or validate API
responses without needing to interact with web UIs. Create separate
applications only when your tests need to interact with third-party web
interfaces (OAuth redirects, payment pages, admin panels).
**Why separate applications:** When your test flow requires interacting with UIs on different domains (your app and the third-party service), you need separate applications because:
* Each application represents a different domain/URL that the browser navigates to
* Browser state (cookies, sessions) doesn't transfer across different domains
* You can configure separate login credentials and test data for each domain
* The test can navigate between your app and the third-party service's UI in a single flow
**Example:**
```
Project: E-commerce Platform
├── Application: Customer Storefront
│ └── Environment: Production (store.example.com)
└── Application: Payment Provider Admin
└── Environment: Production (dashboard.stripe.com)
```
**Test flow example:**
1. **Test in Payment Provider Admin:** "Configure test payment method" (sets up test data in Stripe dashboard)
2. **Test in Customer Storefront:** "Complete checkout with Stripe" (uses the configured payment method, redirects to Stripe, completes payment, returns to your app)
**Strategy:**
* Create login tests for each application (your app and the third-party service)
* Use [Wait For dependencies](/core-concepts/dependencies#application-environment-and-domain-constraints) to ensure the third-party configuration completes before testing your app's integration
* Each test starts with fresh browser state on its target domain (browser state doesn't transfer across domains)
## Decision Guide
If you're unsure which level to use, start by matching your situation to the examples above. If none of the examples fit, use this decision flowchart:
```mermaid theme={null}
flowchart TD
START(["Need to organize testing"]) --> Q1{Can user flows be shared?}
Q1 -->|"No - completely isolated"| NEW_PROJECT["New Project"]
Q1 -->|"Yes - same ecosystem"| Q2{Will the same tests run?}
Q2 -->|"No - different test suite"| NEW_APP["New Application"]
Q2 -->|"Yes - same tests"| NEW_ENV["New Environment"]
NEW_PROJECT --> EX1["Examples: Consultant with separate clients Completely separate product lines"]
NEW_APP --> EX2["Examples: Customer app vs Admin panel Web app vs Mobile app Frontend vs Backoffice"]
NEW_ENV --> EX3["Examples: Dev, Staging, Production Different markets (/en, /de) Preview deployments"]
```
**Golden rule:** Create a new project only when there are no possible shared user flows. Different products within the same team should be different Applications, not Projects.
**Test reuse across applications:** Tests can be reused across different applications in the same project. You can create dependencies between tests in different applications using [Wait For dependencies](/core-concepts/dependencies#application-environment-and-domain-constraints), which pass output data (like user IDs or resource names) between tests.
Browser state (login sessions, cookies) can be shared via Resume From dependencies across applications, but only when both tests use the same Environment. See [Test Dependencies](/core-concepts/dependencies#application-environment-and-domain-constraints) for details on cross-application testing patterns.
## Preview Environments
Preview environments are temporary environments automatically created when you trigger test runs with dynamic URLs via the API. They're perfect for testing pull request deployments, feature branches, and ephemeral CI/CD-created environments.
Preview environments are **automatically created** when you call the [Start Run API](/api-reference/runs/start-test-run) with a URL override in the `applications` parameter:
```json theme={null}
{
"testPlanShortId": "pln_abc123",
"applications": [
{
"applicationShortId": "app_abc123",
"environment": {
"url": "https://pr-123-frontend.vercel.app",
"name": "PR-123-Frontend"
}
}
]
}
```
When QA.tech receives this request:
1. It creates a preview environment with `is_preview: true`
2. Associates it with branch/PR metadata (if provided)
3. Runs your tests against the preview URL
4. The preview environment appears in your application settings
**You don't create preview environments manually** - they're created automatically as part of your CI/CD workflow.
### Viewing and Managing Preview Environments
Preview environments appear in **Settings → Applications & Envs → \[Select Application] → Preview Environments** card.
**Available actions:**
* **Promote to Custom Environment** - Convert a preview environment to a permanent custom environment
* **Delete** - Remove the preview environment (recommended after PRs are merged or branches are deleted)
Preview environments are automatically associated with branch/PR information when created via GitHub Actions or GitLab CI with proper trigger data.
### CI/CD Integration
Preview environments work seamlessly with CI/CD pipelines:
* **[GitHub Actions](/configuration/github-actions)** - Pass preview URLs between jobs
* **[GitLab CI](/configuration/gitlab)** - Use dotenv artifacts to pass preview URLs
* **Any CI/CD platform** - Use the Start Run API with `applications[].environment.url`
For detailed examples, see the [CI/CD Integration Overview](/configuration/ci-cd-integration).
## Test Plans & API Configuration
Test plans allow you to configure which environment and device preset to use per application when running tests. This lets you run the same test cases against different environments (e.g., staging vs production) or with different device configurations (e.g., mobile vs desktop) by selecting different test plans.
**How it works:** When you create a test plan, you can configure application-specific settings that determine which environment URL and device preset to use for each application's tests. These settings are applied whenever the test plan runs, whether triggered manually, via API, or on a schedule. See [Test Plans](/core-concepts/test-plans) for details on creating and configuring test plans.
### Environment Selection
**In Test Plan UI:** Select which environment to use for each application.
**Via API:** You can override environments when triggering runs:
```json theme={null}
{
"testPlanShortId": "pln_abc123",
"applications": [
{
"applicationShortId": "app_abc123",
"environment": {
"url": "https://preview.example.com"
}
}
]
}
```
This allows you to:
* Run the same test plan against different environments
* Test preview deployments dynamically
* Switch between staging and production without changing test plan settings
### Device Preset Configuration
**In Test Plan UI:** Select which device preset to use for each application.
**Via API:** Device preset overrides are supported using the `devicePresetShortId` field in the `applications` array. See [Start Run API](/api-reference/runs/start-test-run) for details.
### Parameter Precedence
When running tests, QA.tech resolves environment and device preset settings in this order:
1. **Project defaults** - Base configuration from application settings
2. **Test plan parameters** - Overrides configured in the test plan UI
3. **Per-run API overrides** - Overrides passed via API at runtime
Later settings override earlier ones. For example, a per-run API override takes precedence over test plan parameters, which take precedence over project defaults.
Use test plan parameters when you want consistent overrides for a specific
test plan. Use API overrides when you need dynamic configuration at runtime
(e.g., preview deployments, CI/CD).
## Finding Short IDs for API Usage
When using the [Start Run API](/api-reference/runs/start-test-run) to override environments, you need Application and Environment Short IDs.
| ID Type | Where to Find | What It's Used For |
| -------------------- | ------------------------------------------------------------------- | -------------------------------------------- |
| Application Short ID | Settings → Applications & Envs (column or dropdown menu) | `applicationShortId` in `applications` array |
| Environment Short ID | Settings → Applications & Envs → \[App] → Environments (above name) | `environment.shortId` field |
For detailed steps, see [Understanding Different IDs](/api-reference/introduction#understanding-different-ids).
## Creating Applications & Environments
Go to [**Settings → Applications &
Envs**](https://app.qa.tech/current-project/settings/applications) in your
project dashboard.
Click the "Add Application" button and fill in the required details
including name, color, icon, and default environment information.
Click "Create Application" to save your application with its associated
environment.
Select the application, then click "Add Environment" to create additional
environments (e.g., staging, production, preview).
# Config Environment Overrides
Source: https://docs.qa.tech/core-concepts/config-environment-overrides
How config environment overrides are stored, merged, and resolved at runtime.
This page explains how config environment overrides are stored, merged, and resolved at run time.
## Overview
Config environment overrides let you assign different payloads for the same config slug based on the selected environment.
The system supports two override layers:
* Environment-scoped overrides via `configsByEnvironment`.
* Plan-wide overrides via `configs`.
When both are present for the same config slug, the plan-wide value wins.
## Add config overrides in the UI
In the SaaS app, you can configure environment-scoped overrides directly on a config:
1. Go to your project's Configs page.
2. Open an existing config and click Edit.
3. In **Environment Overrides**, select the application for this config.
4. Under **Add Environment Override**, choose the environment you want to override and click **Add**.
5. Update the override fields for that environment.
6. Click **Save** on the config form.
After save, the override is saved on that config and linked to the selected environment.
## How project defaults are built
Project-level defaults are constructed from:
1. The application's default environment selection.
2. Config environment overrides, aggregated into a single `configsByEnvironment` map keyed by environment ID, then by config slug.
If multiple configs define overrides for the same environment, each config slug is merged into that environment map.
## Merge order across project, test plan, and run request
Before execution, parameters are merged in this order, with later values overriding earlier values:
1. Project parameters.
2. Test plan parameters.
3. Runtime request parameters (`triggerData.parameters`).
This merged object is written to the blueprint and used during test preparation and execution.
## Effective config resolution order
For a given test and config slug, resolved value precedence is:
1. Base payload from the config.
2. `parameters.configsByEnvironment[environmentId][configSlug]` (if the test has an environment ID and a matching override exists).
3. `parameters.configs[configSlug]`.
This means `configs` always overrides `configsByEnvironment` for the same slug.
## Runtime API shape
`POST /v1/run` accepts config overrides under `parameters`:
```json theme={null}
{
"testPlanShortId": "pln_my-plan_123",
"parameters": {
"configsByEnvironment": {
"b73dc3c3-92b0-4f15-83b0-500c42ecd95c": {
"cfg-login_abc123": {
"username": "preview-user@example.com",
"apiToken": "preview-token"
}
}
},
"configs": {
"cfg-login_abc123": {
"username": "global-user@example.com",
"apiToken": "global-token"
}
}
}
}
```
In this payload, the effective value for `cfg-login_abc123` is the `configs` entry because it has higher precedence.
## Key format and normalization notes
* `configs` and `configsByEnvironment` preserve raw map keys as provided.
* Config identifiers are treated as string keys (typically config short IDs or slugs).
* Environment-specific overrides are only considered when execution has a concrete `environmentId`.
# Configs
Source: https://docs.qa.tech/core-concepts/configs
Manage reusable test variables and data across your tests
## What are Configs?
Configs are sets of data that tests can access and use during execution. Think of them as variables for your test cases - values you want the agent to use automatically. They're commonly used for:
* Login credentials
* Email addresses
* File uploads
**Security Notice: Use Test Credentials Only** Config data (including
passwords and credentials) is **stored unencrypted** and is passed directly to
AI language models during test execution. This is by design to enable the AI
agent to authenticate during testing. **Never use real user credentials or
production passwords.** Always create dedicated test accounts with limited
permissions for your QA.tech tests.
### Configs and variables
If you are looking for variables or environment variables, configs are where
QA.tech keeps them. A config is a named, reusable value (or set of values) that
your tests share, so you do not have to repeat the same data in every test.
Unlike traditional test tooling, there is no `{{variable}}` placeholder syntax
to learn. Configs are resolved by the AI agent. At run time, each config you
attach to a test is passed to the agent together with its name and description,
and the agent decides when and where to use the values.
In your test steps you describe intent rather than hardcoding values. For
example, write "Log in with the configured credentials" instead of typing a
username and password. The agent reads the matching config at run time and fills
in the values. This is also why a clear config name and description matter: they
help the agent pick the right config when several are available.
### Adding a New Config
1. Navigate to Settings » Configs
2. Click "Add config"
3. Select a template
4. Fill in required fields
5. Optionally add a **description** explaining when the config should be used
6. Save your configuration
### Config Descriptions
Each config has an optional **Description** field. Use it to explain when the
config should be used — for example which user role or scenario it represents.
The assistant reads the description (alongside the config name) when deciding
which config or login a test should use, and the test agent also receives it
during execution. A clear description helps disambiguate similar configs without
needing a separate Assistant Rule or Memory entry.
For example, if you have two patient logins, you might describe one as:
> Use this for tests that need a privately-paying patient. Do not use for
> regionally-funded flows.
With that description in place, the assistant can pick the correct login on its
own when generating or running tests.
### Using Configs in Tests
Assign configs to a test case under **Settings » Configs**. When creating or editing a test, select which variables the test should use - the agent receives them automatically at run time.
### System-Provided Configs
QA.tech automatically provides several configs for common testing needs:
1. **Single use Test Email Address**
* Generates a unique email for each test session
* Perfect for email verification flows
* Includes access to [email inbox](/test-features/email-inbox)
2. **Email for Magic Link Login**
* Format: `magic-login-xxxxx@qatech.email`
* Dedicated for magic link authentication flows
* Includes [inbox monitoring](/test-features/email-inbox)
3. **Project e-mail address**
* Format: `prj-xxxxx@qatech.email`
* Project-specific email address
* Permanent address for your project
### Creating Custom Configs
1. **Username + Password Credentials**
* For standard authentication
* Fields: Username (can be email) and password
* Optional "Use for Basic Auth" checkbox for HTTP Basic Authentication
The "Use for Basic Auth" checkbox is for **HTTP Basic Authentication**,
which is different from regular login forms. **Enable this when your tests
need to access:** - Staging or testing environments protected with browser
authentication popups - URLs that trigger browser dialogs asking for
username/password - Corporate proxies or internal tools requiring basic
authentication - Password-protected development environments When enabled,
QA.tech automatically supplies these credentials to the browser whenever it
encounters an HTTP Basic Auth challenge, so your tests can proceed without
manual intervention.
**Important:** This is for the HTTP Basic Authentication protocol (RFC
7617\), not for testing login forms on your website. For regular login
forms, create a Username + Password config and leave "Use for Basic Auth"
disabled.
2. **Username + Password Credentials - with Two-Factor Authentication**
* For two-factor authentication flows
* Includes 2FA setup (Google Authenticator/Authy)
3. **Valid Email + Password Login Credentials**
* Includes generated unique email
* Password management
* [Email inbox access](/test-features/email-inbox)
4. **File Upload**
* Upload static files for testing
* Maximum size: 250MB
* Supports any file type (no MIME type or extension restrictions)
5. **Custom Fields**
* Reusable key-value pairs for any data the agent might need, such as a name,
phone number, address, or token
* Each field has a key and a value, and you can add as many fields as you need
* Field keys must be unique within the config (case-insensitive)
* The agent receives each field as a `key: value` line, alongside the config
name and description
### Custom Fields Example
A **Custom Fields** config named "Patient details" with fields for `fullName`,
`phone`, and `memberId` reaches the agent as:
```
Patient details (Use for privately-paying patient flows):
fullName: Jane Doe
phone: +1-555-0100
memberId: PAT-4421
```
In your test you can then write "Register a new patient using the configured
patient details" and the agent will use these values.
# Crawling Sessions
Source: https://docs.qa.tech/core-concepts/crawling
Learn how crawling sessions help QA.tech understand your application and improve test suggestions
# Crawling Sessions
Crawling sessions help QA.tech learn about your application by exploring pages and interactions. This improves test suggestions and agent performance.
## Overview
Manual crawling allows you to explore specific areas of your application. Use this when:
* Launching new features
* Needing focused exploration (e.g., admin panels, checkout flows)
* Updating navigation or UI structure
* Improving test coverage for critical user journeys
Navigate to [**Settings → Crawling**](https://app.qa.tech/current-project/settings/crawling) to access the Crawling Sessions page.
## Configuration
### Output State (Optional)
Resume from a test case's final browser state. Useful for:
* Crawling authenticated areas (use a login test's output state)
* Exploring flows requiring specific setup
### Start URL
The URL where crawling begins (e.g., `https://app.example.com`).
### Maximum Depth
How many link levels deep to crawl from the start URL.
* **0**: Only the starting page
* **1**: Starting page + directly linked pages
* **2+**: Progressively deeper exploration
**Range**: 0-10
**Default**: 1
### Maximum Actions
Maximum number of interactions (clicks, navigations) during the session.
**Range**: 1-1,000
**Default**: 300
### Crawling Intent (Optional)
Describe what to focus on (up to 500 characters). The crawler prioritizes actions matching your intent.
**Examples**:
* `Focus on login and authentication flows`
* `Explore product catalog and filtering options`
* `Crawl admin panel and user management`
## Understanding Results
### Session Status
* **Processing**: Currently running
* **Complete**: Successfully finished
* **Failed**: Encountered an error
* **Cancelled**: Manually stopped
### Iteration Details
Each crawling iteration shows:
* **Screenshot**: Visual capture of the page
* **Source Action**: How the crawler reached this page ("n/a" for start page)
* **Depth**: Links away from start URL (0 = start page)
* **Found Actions**: Interactive elements discovered (links, buttons, forms)
* **Intent Score**: Relevance to your goal (0-40% low, 41-70% moderate, 71-100% high)
## Best Practices
* **Be specific**: Clear intents work better (❌ "Crawl everything" → ✅ "Focus on e-commerce checkout flow")
* **Use output states**: Chain a login test to explore authenticated areas
* **Review results**: Check screenshots and intent scores to verify exploration
## Troubleshooting
**Crawl Failed**
* Requires authentication → Use output state from login test
* Pages not accessible or SSL issues
# Test Dependencies
Source: https://docs.qa.tech/core-concepts/dependencies
Control test execution order, manage browser isolation, and structure multi-user testing scenarios
**"Browser session"** = an isolated browser context (BrowserContext) with its
own separate cookies, localStorage, and sessionStorage. **"Browser state"** =
the data stored within a session (login cookies, localStorage values,
sessionStorage, etc.)
**Use dependencies when:** You're testing workflows (i.e. login → create → edit), need to maintain login sessions across tests, or need to pass data (like created user IDs) between tests. Dependencies ensure tests run in the right order and let you reuse expensive setup like authentication.
Isolated tests not dependent on each other run in parallel (each in its own isolated browser session), so QA.tech can execute many tests simultaneously when no [concurrency limit](/core-concepts/parallel-test-execution) is set.
Tests within a dependency chain run sequentially, meaning you'll get faster results with independent tests that are used for testing isolated features and don't depend on each other.
In practice you will have to use both - independent tests and tests with various combinations dependencies to cover functionality of your Application.
## Dependency Types
### Resume From
**What it does:** Test inherits browser state (login sessions, cookies, localStorage, sessionStorage) AND output data from the dependency. If the dependency completed successfully within the last 6 hours in the same environment, state is reused immediately. If older or failed, the dependency runs first.
**Use when:** You want to avoid repeated logins or expensive setup by reusing browser state from a previous test in the chain.
### Wait For
**What it does:** Test waits for dependency to complete before starting. Receives output data (user IDs, resource names, URLs) from the dependency test, but starts with a fresh browser session-no shared cookies, localStorage, or login state.
**Use when:** You need execution order and data passing between different user contexts, or when tests need created data but not browser state. For workflows with the same user, use Resume From instead to avoid repeated logins.
## Multiple Dependencies
A test can have multiple dependencies using both "Wait For" and "Resume From" relationships. If any dependency fails, all dependent tests are automatically skipped in that test run.
* Each test can have exactly **one Resume From** dependency (single browser
state parent) - Each test can have **multiple Wait For** dependencies
(coordinate with many tests) - **Mobile apps:** Resume From is not supported.
Use Wait For when you need execution order between mobile tests. See [Mobile
App Testing](/test-features/mobile-app-testing#ai-chat-and-test-dependencies).
## Examples
### Resume From Example
**Test structure:**
1. **Login Test** → 2. **Create Order Test** → 3. **View Dashboard Test**
All three tests share the same browser session, so the browser state (login cookies, localStorage, etc.) is preserved throughout the chain.
### Wait For Example
**Test structure:**
1. **Create Team Test** (outputs: team name "Marketing Team")
2. **Add User to Team Test** (receives: team name from Test 1)
Tests run sequentially, but each starts with a fresh browser session. Test 2 receives the output data from Test 1.
## Browser Isolation & Parallel Execution
Each independent test chain runs in its own isolated browser session. Tests within a chain run sequentially and share browser state through "Resume From" dependencies, while different chains run in parallel with complete isolation.
```mermaid theme={null}
graph TB
subgraph "Browser Session A"
T1["Test 1: Login User 1 (No dependencies - NEW SESSION)"]
T2["Test 2: Create Dashboard (Resume From Test 1)"]
T3["Test 3: Edit Dashboard (Resume From Test 2)"]
T1 -->|SequentialShares Session | T2
T2 -->|SequentialShares Session | T3
end
subgraph "Browser Session B"
T4["Test 4: Login User 2 (No dependencies - NEW SESSION)"]
T5["Test 5: View Dashboard (Resume From Test 4)"]
T4 -->|SequentialShares Session | T5
end
T3 -.->|Wait ForData Only | T5
```
**Key points:**
* Session A (Tests 1→2→3) runs sequentially with shared User 1 login
* Session B (Tests 4→5) runs sequentially with shared User 2 login
* Both sessions run in parallel, completely isolated from each other
* Test 5 waits for Test 3 to complete and receives output data, but maintains separate browser state
**"Run Tests" Behavior:** When you click "Run Tests" or trigger a test plan: -
Independent test chains run in parallel with isolated browser sessions - Tests
within a chain run sequentially, maintaining dependency order - The system
auto-scales when no limit is set - Optional [per-environment concurrency
limits](/core-concepts/parallel-test-execution) cap how many tests run at
once; extra tests wait in queue
## Multi-User Testing Scenarios
When testing features that require multiple users to be logged in simultaneously (e.g., collaboration, sharing, real-time features), create separate dependency chains for each user.
### Pattern: Testing Collaborative Features
**Scenario:** Test that User 1 can create and share a visualization with User 2, who can then view it.
**Structure:**
**User 1 Chain:**
1. **Login as User 1** (root test, config: `user1@example.com`)
2. **Create Visualization** (Resume From: Login as User 1)
3. **Share Visualization** (Resume From: Create Visualization)
**User 2 Chain:**
1. **Login as User 2** (root test, config: `user2@example.com`)
2. **View Shared Visualization** (Resume From: Login as User 2, Wait For: Share Visualization)
```mermaid theme={null}
graph TB
subgraph "User 1 Chain > Browser Session A"
U1L["Login as User 1 (Config: user1)"]
U1C["Create Visualization (Resume From Login)"]
U1S["Share Visualization (Resume From Create)"]
U1L -->|Resume FromShares Session | U1C
U1C -->|Resume FromShares Session | U1S
end
subgraph "User 2 Chain Browser Session B"
U2L["Login as User 2 (Config: user2)"]
U2V["View Shared Visualization (Resume From Login)"]
U2L -->|Resume FromShares Session | U2V
end
U1S -.->|Wait ForPasses visualization link | U2V
```
**How It Works:**
* User 1 chain runs in browser session A (all three tests share User 1's login)
* User 2 chain runs in browser session B (both tests share User 2's login)
* Sessions A and B are completely isolated from each other
* Both users remain logged in throughout their respective chains
* "Wait For" ensures User 2's test waits for sharing to complete
* Output data (visualization link) passes from User 1 to User 2's test
* Browser state (login sessions) remains isolated between chains
**Key Points:**
* Each user gets their own root login test with separate test configuration
* Use different test configurations for different user credentials
* Use "Resume From" within each user's chain to maintain their session
* Use "Wait For" between chains to coordinate timing while keeping sessions isolated
* Tests in different chains run in parallel but maintain complete isolation
Each user needs their own root login test to ensure they get an isolated
browser session. Depending on another user's login will cause the tests to
share a session, and one user will log out the other.
## Application, Environment, and Domain Constraints
Browser state can only be shared via Resume From when tests run in the **same Environment**. This applies even across different Applications - Resume From works between Applications if both tests use the same Environment.
### What Works vs. What Doesn't
| ✅ Supported | ❌ Not Supported |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Resume From within the same Environment | Resume From across different Environments |
| Output data passed between any tests, any Application, any Environment | Browser state transfer across different domains |
| Wait For dependencies across different Applications | Cookie/localStorage transfer between `app.example.com` and `admin.example.com` |
| Resume From across different Applications in the same Environment | Using Resume From between staging and production Environments |
### Testing Across Applications
**Use Wait For dependencies across Applications:**
* Tests execute sequentially without browser state transfer
* Output data (user IDs, team names) passes between any tests
* Each test starts with fresh browser state on its target URL
**Example:**
```
Test 1: Create User (Frontend Application) → outputs userId
Test 2: Verify User (Admin App Application) → waits for Test 1, receives userId
```
**Resume From only works within the same Environment:**
* The system requires both tests to use the same Environment for state reuse
* Even if two Environments have similar URLs, Resume From won't work across different Environments
* Tests in different Applications CAN use Resume From if they're in the same Environment
## Data Sharing Between Tests
QA.tech provides two mechanisms for passing data between dependent tests. Understanding when to use each helps you build effective multi-step test workflows.
### Output Values (Recommended for Cross-Session Data)
Output values are data explicitly saved by a test during execution. They can be passed to any dependent test, regardless of whether the tests share a browser session.
| Aspect | Details |
| ------------------- | ---------------------------------------------- |
| **What's shared** | Text data: URLs, IDs, names, any string value |
| **Works with** | Both Wait For and Resume From dependencies |
| **Persists across** | Different browser sessions (cross-application) |
| **When saved** | Agent saves during test evaluation |
| **How received** | Agent sees values in context when test starts |
**Use output values when:**
* Sharing URLs between tests that run in different browser sessions
* Passing created resource IDs (user IDs, order numbers) to verification tests
* Coordinating data between tests in different applications
**Example: URL Sharing**
1. Test A creates a resource and the agent saves the URL in output values
2. Test B has a Wait For dependency on Test A
3. When Test B runs, the agent receives Test A's URL and can navigate to it
### Clipboard (Same Session Only)
Clipboard values are automatically saved when a test completes and restored when a Resume From dependent test starts in the same browser session.
| Aspect | Details |
| ------------------- | ------------------------------------------- |
| **What's shared** | Clipboard content at test completion |
| **Works with** | Resume From dependencies only |
| **Persists across** | Same browser session chain only |
| **When saved** | Automatically when test finishes |
| **How received** | Clipboard is pre-populated when test starts |
**Use clipboard when:**
* Copying and pasting within the same browser session chain
* Quick value transfer between sequential tests sharing a session
**Example: Session Chain**
1. Test A logs in and copies a verification code to clipboard
2. Test B uses Resume From dependency on Test A
3. Test B can paste the verification code (same browser session, clipboard restored)
### Comparison Table
| Mechanism | Cross-Application? | Cross-Session? | Automatic Save? |
| ------------- | ------------------------ | ---------------------- | --------------------------- |
| Output Values | Yes | Yes | No - agent saves explicitly |
| Clipboard | Only if same Environment | No - same session only | Yes - on test completion |
### Common Patterns
#### Pattern 1: URL Sharing Across Applications
**Scenario:** Test in App 1 creates a resource, Test in App 2 needs to access it.
```
Test A (App 1): "Create order" → saves order URL in output values
↓ Wait For (data only)
Test B (App 2): "Verify order in admin" → receives URL, navigates to it
```
Both tests run in separate browser sessions on different applications. Output values bridge the gap.
#### Pattern 2: Session Chain with Clipboard
**Scenario:** Multi-step workflow in the same session.
```
Test A: "Login and copy auth code"
↓ Resume From (session + clipboard)
Test B: "Paste auth code and verify"
```
Both tests share the browser session. Test B receives the clipboard value automatically.
#### Pattern 3: Combining Both Mechanisms
**Scenario:** Need both shared data AND session continuation.
```
Test A: "Create resource" → saves URL in output values
↓ Resume From (gets session + clipboard)
Test B: "Verify resource" → receives URL from output values, keeps login session
```
Resume From provides session continuity while output values provide the data.
When a test has both Resume From and Wait For dependencies, it receives: -
Browser state (cookies, localStorage) from the Resume From dependency - Output
values from ALL dependencies (both Resume From and Wait For)
# Issues
Source: https://docs.qa.tech/core-concepts/issues
QA.tech automatically detects issues during test runs, including failed tests, JavaScript console errors, and WCAG accessibility violations.
## What types of issues does QA.tech detect?
QA.tech automatically detects issues during test runs. You can export any detected issue to Linear, Jira, or Trello.
* **Failed tests** - When a test cannot be completed, QA.tech creates a failed test issue. This often indicates a UX problem or functional issue in your application that needs investigation.
* **Console errors** - QA.tech captures JavaScript console errors that occur during test execution. These are typically filed as low-severity issues but can indicate bugs or misconfigurations.
* **Accessibility issues** - QA.tech automatically checks for WCAG 2.0, 2.1, and 2.2 violations using [axe-core](https://github.com/dequelabs/axe-core) on every page your tests visit.
#### How it works
* Issues are created automatically when tests fail during execution
* Same issue across multiple test runs is grouped together using fingerprint-based deduplication
* Issues appear in the dashboard with details about when and where they occurred
#### WCAG coverage
* WCAG 2.0 (Level A, AA, AAA)
* WCAG 2.1 (Level A, AA)
* WCAG 2.2 (Level AA)
#### Limitations
**Accessibility scanning:**
* Maximum 10 issues captured per test session ([see limits](/core-concepts/limits))
* Color contrast not checked (use browser DevTools for this)
* Content in iframes not scanned
* Certain rules disabled: `region`, `landmark-one-main`
**What we don't detect:**
Because we interact with your product as a user through a browser:
* **Server-side errors:** Use a monitoring tool like Sentry or BugSnag for backend exceptions
* **Network request failures:** We log these for debugging but don't create issues for them
**Automatic scanning:** Accessibility checks run automatically whenever you execute a test - no setup needed.
**Schedule regular checks:** To get ongoing accessibility monitoring, create a [test plan](/core-concepts/test-plans) with your test cases and add a schedule (daily, weekly, etc.), or run tests from your [CI/CD pipeline](/configuration/ci-cd-integration).
## How Issues Are Collected
Issues are automatically detected and created during test runs. You don't need to manually create or report issues.
### Automatic Detection
When a test execution completes with a **FAILED** result, QA.tech automatically:
1. Creates an issue record for the failed test
2. Links the issue to the specific test case and run
3. Groups similar issues together using fingerprint-based deduplication
### Deduplication
QA.tech uses a fingerprint system to group similar issues together:
* Each issue has a unique fingerprint based on the test case and project
* If the same test fails across multiple runs, it creates one issue with multiple occurrences
* This prevents duplicate issues and helps you track how often a problem occurs
### When Issues Are Created
* **After test execution:** Issues are created automatically when a test finishes with a FAILED result
* **Requires test run:** Issues are only created for actual test runs (not during initial test generation)
* **One issue per test case:** Each unique test case failure creates one issue, with multiple occurrences tracked over time
## Issues Dashboard
### Filtering Issues
Use filters to find specific issues:
| Filter | Options |
| ------------ | -------------------------------------------------------------------- |
| **Severity** | Minor, Major, Critical |
| **Type** | Accessibility, Console, Failed Test, Hosting |
| **Status** | Active, Ignored, Resolved (by default, only Active issues are shown) |
You can also use the search bar to find issues by title or description.
### Insights Dashboard
Issues also appear in the **Insights** page with date range filtering:
* **Issues header stats:** Shows the count of new issues and issue occurrences within the selected date range
* **New Issues table:** Displays active issues that were created during the selected time period
* **Date filtering:** Use the date range picker to view issues from specific time periods
The Insights dashboard provides a quick overview of issues trends, while the full Issues page offers complete management capabilities.
### Viewing Issue Details
Click on any issue to see:
* **Severity and Status** - How critical the issue is and its current state
* **First/Last Seen** - When the issue was first and most recently detected
* **Occurrences** - Every time this issue was encountered during test runs, including which test run and URL
* **Help Information** - Guidance on how to fix the issue (accessibility issues include WCAG documentation links)
## Export to Issue Trackers
QA.tech integrates with popular issue tracking tools so you can manage bugs in your existing workflow. Issues are **not automatically synced** - you choose which issues to export.
### Supported Integrations
| Integration | What Gets Created | Configuration |
| ------------------------------ | ----------------- | ----------------------------- |
| [Jira](/integrations/jira) | Jira Issue | Select project and issue type |
| [Linear](/integrations/linear) | Linear Issue | Select team |
| [Trello](/integrations/trello) | Trello Card | Select board and list |
### How to Export an Issue
Click **Issues** in the sidebar to see all detected issues for your project.
Use filters to narrow down by severity, type, or status. Search for specific
issues by title.
Click on an issue to view its full details, occurrences, and help
information.
In the issue detail view, find the **Link** section at the top. Click the
export button for your configured integration:
* **"Create ticket"** (Jira) — creates a Jira issue with the `qatech` label
* **"Create issue"** (Linear) — creates a Linear issue in your team
* **"Create Card in Trello"** — creates a Trello card in your list
From a test run, you can also export via **Send to Jira**, **Send to Linear**, or **Send to Trello** in the tracer.
If you see **"Add Integration"** instead of export buttons, you need to
configure an integration first. Go to **Settings → Integrations** in your
project to set up Jira, Linear, or Trello.
### What Gets Exported
When you export an issue, QA.tech sends:
* **Title:** Prefixed with the QA.tech issue short ID (e.g. `[iss_abc] Login button stays disabled`)
* **Description:** Summary, test intent, test steps, what went wrong, evaluator notes, evidence (screenshots and console details), and a link back to QA.tech
* **Labels (Jira):** `qatech` for filtering exported tickets
* **Console Details:** For console errors, includes the error level, file location, line numbers, and messages
See [Jira](/integrations/jira) or [Linear](/integrations/linear) for tracker-specific details.
After exporting, the issue detail page shows a direct link to the created
ticket, so you can quickly jump to it in Jira, Linear, or Trello.
# Knowledge
Source: https://docs.qa.tech/core-concepts/knowledge
Manage project knowledge and context for better AI assistance
QA.tech's knowledge system helps AI assistants understand your project and helps the test agent run reliably. **Assistant** knowledge shapes how the chat assistant answers questions and creates tests, while **Test Agent** knowledge guides the agent as it executes those tests. Together they bring more accurate test generation and better project-specific guidance.
## Settings → Knowledge
Knowledge lives in a sidebar group under [**Settings → Knowledge**](https://app.qa.tech/current-project/settings/knowledge), split into three tabs. Opening **Settings → Knowledge** lands you on the **Assistant** tab.
### Assistant tab
Knowledge used by the [AI Chat Assistant](/core-concepts/ai-chat-assistant) and during test creation.
#### Project Summary
An AI-generated summary of your website's functionality and structure, created by analyzing your pages and user interactions. The summary is always part of chat context and updates as your site evolves.
**What's Included:**
* Overview of your platform's purpose and main functionality
* Key features and user flows
* Important pages and their purposes
* Integration points and external services
**To Refresh the Summary:**
Navigate to [**Settings →
Knowledge**](https://app.qa.tech/current-project/settings/knowledge).
Find the **"Project Summary"** section on the **Assistant** tab.
Click **"Refresh"** to generate an updated summary based on your latest site
analysis.
The summary requires initial site analysis to be completed. If no summary
appears, ensure you've run some tests or site analysis first.
#### Assistant Rules
Rules the chat assistant always follows when answering questions and creating tests. See [Rules](#rules) below.
#### PR Review Rules
Rules applied during PR review. See [Rules](#rules) below.
Use **Browse Library** on the PR Review Rules section to add curated templates (cross-device coverage, auth dependencies, ticket acceptance criteria, and more). Templates are copied into your project as normal rules — edit or remove them anytime. Add custom rules with **Add Rule** when you need project-specific guidance the library does not cover.
#### Library
Link and text knowledge items that AI assistants can reference when generating tests and answering questions. Library items are stored at the project level and shared across all team members.
**What You Can Add:**
* **URLs**: Link to documentation sites, help pages, API docs. URLs are crawled and indexed.
* **Text Content**: Add custom instructions, project notes, or guidelines. Text items are stored and summarized.
See [Adding Library Items](#adding-library-items) for steps.
#### Memories
Knowledge items the assistant creates automatically from your conversations, capturing useful facts it learns as you work. You can review, edit, and delete memories from the **Assistant** tab so the assistant keeps an accurate picture of your project.
### Test Agent tab
Knowledge used by the test agent during test execution.
#### Agent Rules
Rules the test agent follows while executing tests. Agent Rules support **targeting filters** - by application, labels, scenario, or URL path - so a rule applies only to matching tests instead of every test in the project. See [Rules](#rules) below.
#### Agent Visuals
Image uploads that help the agent visually identify UI elements during execution. Add screenshots of buttons, icons, or layouts the agent should recognize so it can act on them reliably.
### Crawling tab
Manage crawling sessions, your project domains (suggested domains and excluded paths), and the **Reset Knowledge Graph** action. See [Crawling Sessions](/core-concepts/crawling) for how crawling builds the agent's understanding of your application.
## Rules
Rules are how you give the AI persistent instructions that should always apply. There are three kinds, each used at a different stage:
* **Assistant Rules** - followed by the chat assistant during chat and test creation.
* **PR Review Rules** - applied during PR review. Start from the built-in rule library for common patterns (device presets, login dependencies, monorepo apps), then customize.
* **Agent Rules** - followed by the test agent during test execution. Agent Rules support targeting filters (by application, labels, scenario, or URL path) so a rule applies only to matching tests.
As a guideline: execution-relevant guidance belongs in **Agent Rules**, while creation-relevant guidance belongs in **Assistant Rules**.
**Good Rules - Use Cases with Examples:**
| Use Case | Example | What It Achieves |
| ------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------- |
| Domain knowledge | "This is a railway booking system for American routes. Stations use AMTRAK codes." | Agent understands domain terminology |
| Quality standards | "Tests should fail if obvious typos or broken layouts are detected." | Agent enforces quality expectations |
| Data requirements | "When creating test users, always use Swedish names and addresses." | Agent generates appropriate test data |
| UI conventions | "Red buttons indicate destructive actions - verify confirmation dialogs appear before clicking." | Agent handles UI patterns correctly |
| Auth flows | "Login requires SMS 2FA. The code will be available in the test inbox." | Agent knows to check inbox for 2FA |
| Timing expectations | "Page loads may take up to 10 seconds in staging. Wait for loading indicators to disappear." | Agent waits appropriately |
| Known limitations | "The checkout flow is broken on mobile viewports - skip mobile tests for checkout." | Agent avoids known issues |
**Best Practices:**
* Keep rules concise but specific - Agent Rules are consulted on every relevant action.
* Focus on guidance that applies broadly; use Agent Rule targeting filters when something should apply only to certain tests.
* Include domain-specific terminology and behaviors.
* Mention any unusual UI patterns or interactions.
* Update rules when your product behavior changes.
**What NOT to Include:**
* **Test-specific instructions** - put these in test steps instead
* **Credentials** - use [Configs](/core-concepts/configs) instead
* **URLs** - use [Applications and Environments](/core-concepts/applications-and-environments) instead
* **Temporary workarounds** - document in test steps where needed
## How Knowledge Works
Knowledge has different roles during test creation versus test execution:
| Knowledge Type | Test Creation (Chat) | Test Execution |
| ------------------------------- | --------------------------------------- | ------------------------------------------------------------- |
| Library (link/text items) | Used - searched semantically | Not used (unless attached to the test) |
| Memories | Used | Not used (unless attached to the test) |
| Knowledge Graph | Used - site structure + semantic search | Not used |
| Project Summary | Used | Used (via agent context) |
| Assistant Rules | Used | Not used |
| Agent Rules | Not used | Used - every relevant decision (subject to targeting filters) |
| Agent Visuals | Not used | Used |
| Chat file uploads (attachments) | Used in that chat only | Not used |
**During Test Creation (Chat & Test Generation):**
* The assistant searches your knowledge on demand - semantic search across your Library, Memories, and the site/feature knowledge graph - rather than stuffing everything into the prompt.
* The Project Summary provides overall context about your application.
* Assistant Rules shape how the assistant answers and what tests it proposes.
* The AI generates **test steps** based on all the above context.
**During Test Execution:**
Only a targeted subset of knowledge is available to the test agent:
* Knowledge items **explicitly attached to the test** (text and memory items only; links are excluded as too large)
* **Agent Visuals**
* **Agent Rules** matching that test (subject to targeting filters)
* The **Project Summary**
Semantic search does **not** run at execution time. The agent works from the test's goal, steps, and the targeted context above. [Configs](/core-concepts/configs) provide credentials, environment URLs, and test files.
This separation means you can safely update knowledge without affecting tests
that are already running or scheduled.
### Attaching Knowledge to a Specific Test
Library and Memory items aren't used during execution by default. To give the agent a specific item as context while a test runs, **attach the knowledge item to that test case**. This is the supported way to make a Library or Memory item available during execution (text and memory items only; links are excluded as too large).
### Project Knowledge vs. Chat-Local Files
Understanding the scope of your knowledge helps you organize it effectively:
**Project Knowledge** (Settings → Knowledge):
* **Shared across all chats** in the project
* **Accessible to all team members**
* **Permanent and searchable** by semantic search
* **Best for:** Official documentation, stable specifications, product rules
**Chat-Local Files** (uploaded directly to a chat):
* **Per-message attachments** scoped to a single conversation
* **Other users' chats cannot access them**
* **Not persisted as project knowledge** - they do not automatically become reusable Library items
* **Best for:** Experimental specs, PR descriptions, feature branch docs, exploratory work
This isolation prevents work-in-progress information from affecting other team members. When your experimental work becomes final, add it to the Library or ask the AI Chat to create a knowledge item from the conversation.
### Knowledge Graph & Semantic Search
Crawled documentation and indexed knowledge are stored in a [Knowledge Graph](/core-concepts/knowledge-graph) with embeddings that enable semantic search - finding relevant information based on meaning rather than exact keywords.
During chat and test creation, the assistant searches your knowledge automatically and on demand:
1. Analyzes your request to understand intent
2. Searches your Library, Memories, and the site/feature knowledge graph for semantically similar content
3. Provides the most relevant information to the AI assistant
4. Uses clear titles to improve search accuracy
The **Reset Knowledge Graph** control lives on the [Crawling](#crawling-tab) tab.
## Adding Library Items
Navigate to [**Settings →
Knowledge**](https://app.qa.tech/current-project/settings/knowledge) and
open the **Assistant** tab.
Find the **Library** section and click **"Add Knowledge"**.
Choose your content type - **URL** or **Text** - based on what you want to
add.
Fill in the title and content for your knowledge item. Use clear,
descriptive titles that reflect the content - this helps both the AI search
and allows you to reference specific knowledge by name in chat (e.g., "use
the API documentation knowledge").
Save to process and index the content for use by AI assistants.
### Organization Tips
* **Use Clear Titles**: Make Library items easy to find with specific, descriptive names
* **Keep Content Current**: Regularly review and update documentation links
* **Remove Outdated Info**: Delete or update knowledge that no longer reflects your product
* **Test Your Knowledge**: Ask the chat assistant questions to verify it finds the right information
* **Avoid Contradictions**: When updating specs, remove old versions to prevent confusion
## Troubleshooting
**Knowledge not being found in chat:**
* Ensure content has been fully processed (check status indicators)
* Try different search terms or questions
* Verify the content is relevant to your question
* Use clear, descriptive titles for better semantic search results
**Rules not being applied:**
* Check that you've saved the rule on the correct tab (Assistant Rules, PR Review Rules, or Agent Rules)
* For Agent Rules, verify the rule's targeting filters (application, labels, scenario, URL path) actually match the test you're running
* Review test results to see whether the rule's guidance is being followed
**Summary not generating:**
* Ensure you've completed initial site analysis
* Try running a few tests to build the knowledge graph
* Manually refresh the summary after adding new functionality
**Chat-uploaded files not visible to team:**
* Remember: files attached in chat are conversation-specific
* To share with team, add the content to the Library instead
* Other team members need to attach it to their own chats, or use project knowledge
# Knowledge Graph
Source: https://docs.qa.tech/core-concepts/knowledge-graph
A knowledge graph is a digital representation of facts and relations. This can be used as an AI agent’s understanding and memory of a website.
## Introduction
For a human, the knowledge graph would amount to all of their experiences on your website, and other sites like it. Take a webshop for example: If you visit a new online store, you already have a lot of existing knowledge about how you expect it to work and behave. You know that you most likely will be able to add things to the cart, check out with your credit card, and enter your shipping address.
Instinctively you will look for the cart in the top right corner, and if you can’t find it there you’ll look in a few other places where you have seen a cart icon before on other websites.
If you don’t find the cart in an intuitive location, it could almost be considered a bug.
If you find the cart after some searching you will most likely remember it, and have an idea of where it should be the next time you visit the site.
A computer program, an AI Agent, does not have these intuitive human associations. We solve this problem using a knowledge graph.
## QA.tech’s knowledge graph
We create a large graph of all the interactions we can perform on your website. If you are familiar with sitemaps, this is like a sitemap on steroids. We then combine that graph with prior knowledge of similar sites, and use that to guide our agent as it makes decisions during testing.
What data do we track?
We only track the data that a regular user would see as they interact with your web application. If you provide login credentials, we will use those and ingest the information behind that login screen. This gives you full control over what our AI agent has access to.
How does the graph look?
Let’s take a look at an example graph for one of our customers.
The nodes in blue are different pages in the product, the purple ones are interactions that the agent has taken in the past
## Further reading
[https://qa.tech/blog/knowledge-graphs-for-ai-agents/](https://qa.tech/blog/knowledge-graphs-for-ai-agents/)
# Limits
Source: https://docs.qa.tech/core-concepts/limits
Understanding log limits and how they affect your tests
# Limits
QA.tech implements rate limits to ensure system stability and optimal performance during test execution. These limits prevent excessive resource consumption and ensure consistent test behavior.
## Default Limits
Each test execution has the following limits per default:
| Type | Default Limit | Description |
| -------------------- | ------------- | ----------------------------------------------------------- |
| Console logs | 100 | Maximum number of console log entries captured per session |
| Network requests | 1000 | Maximum number of network requests captured per session |
| Accessibility issues | 10 | Maximum number of accessibility issues captured per session |
## What Happens When a Limit is Reached
When a limit is reached:
1. A warning message is logged indicating the limit has been reached
2. Further events of that type are not captured or stored
3. Test execution continues normally without additional event collection of the limited type
For example, if your application generates 150 console logs, only the first 100 will be captured and displayed in the test results.
## Customizing Limits
If your testing needs require higher limits, we can increase these on request. Contact our support team to discuss your requirements.
Increasing rate limits may impact test performance and result in larger test
reports. We'll work with you to find the optimal balance for your specific
needs.
## Best Practices
To work effectively with rate limits:
1. **Filter unnecessary logging**: Reduce verbose console logging in test environments
2. **Split complex tests**: Break tests with many interactions into smaller, focused test cases
# Notifications
Source: https://docs.qa.tech/core-concepts/notifications
QA.tech notifies your team when test runs complete and provides weekly summaries of testing activity. Configure notifications at the project level for defaults, at the test plan level for customization, or per-run via API for CI/CD workflows.
## Understanding Notifications
### Notification Types
| Type | Purpose | Timing | Channels |
| -------------- | ---------------------------- | --------------------- | ----------------------------- |
| Run Completion | Alert team when tests finish | Immediately after run | Slack, Email, Microsoft Teams |
| Weekly Summary | Summarize testing activity | Mondays 6 AM CET | Email only |
### Delivery Channels
| Channel | Project Level | Test Plan Level | Per-Run API |
| --------------- | --------------- | ---------------- | ------------------------------------ |
| Slack | Default channel | Override channel | Override channels |
| Email | - | Add recipients | Override recipients |
| Microsoft Teams | Webhook URL | Webhook URL | notifyOn / disable (project webhook) |
### Configuration Levels
```
Project Settings (defaults for all runs)
└── Test Plan Settings (override per test plan)
└── Per-Run API / Run with Options (override per individual run)
```
* **Project Level** - Set default Slack channel and Teams webhook. All runs use these unless overridden.
* **Test Plan Level** - Override Slack channel, add email recipients, configure Teams webhook per test plan.
* **Per-Run** - Override Slack, email, or Teams when triggering a run via API or **Run with Options**. Use `type: "none"` to suppress all notifications for that run.
## What You Receive
### Run Completion Notifications
All run completion notifications (Slack, Email, Teams) include:
| Content | Description |
| -------------- | --------------------------------------------------- |
| Run status | Pass or fail with visual indicators |
| Test plan name | If run was part of a test plan |
| Timing | When started and total duration |
| Test summary | Total tests, failed count, error count |
| Failed tests | Up to 5 failed tests with names and failure reasons |
| Results link | Direct link to full test report |
You receive a notification for every completed run - whether triggered manually, through CI/CD, or via schedule.
### Weekly Summary Reports
Weekly summaries are separate from run completion notifications. They provide organization-wide metrics to all members.
**When**: Every Monday at 6 AM Central European Time\
**Who**: All organization members (unsubscribe via email footer)
**Contents**:
* Tests executed count
* Pass percentage
* Time saved estimate
* Suggested new tests to add
* Summary of test runs
## Common Patterns
### Team-Wide Slack Alerts
**Goal**: Everyone on the project sees test results in a shared channel.
**Setup**: Configure default Slack channel at Project Settings → Integrations → Slack. All runs notify this channel automatically.
### Stakeholder Email Notifications
**Goal**: Product managers or executives receive email updates for specific test plans.
**Setup**: Open test plan → Notification settings → Add email recipients. Choose "Only on failure" to reduce email volume.
### CI/CD-Triggered Notifications
**Goal**: Tests triggered via GitHub Actions or GitLab CI notify the team automatically.
**Setup**: Runs triggered via API use project defaults. No additional configuration needed - notifications flow to configured channels.
**Guides**: [GitHub Actions](/configuration/github-actions) | [GitLab CI](/configuration/gitlab) | [Start Run API](/api-reference/runs/start-test-run)
### PR-Specific Slack Channels
**Goal**: Route notifications for a specific PR to a dedicated Slack channel.
**Setup**: Use per-run API overrides to specify channels dynamically:
```json theme={null}
{
"testPlanShortId": "abc123",
"notifications": [
{ "type": "slack", "channel": "C0478ABCDEF", "notifyOn": "always" }
]
}
```
### Failure-Only Routing
**Goal**: Reduce noise by only notifying on failures.
**Setup**:
* **Email/Teams**: Set "Only on failure" in test plan notification settings, or pass `"notifyOn": "failure"` in a per-run override
* **Slack**: Use `"notifyOn": "failure"` in the notifications array
## Decision Guide
Use this flowchart to determine where to configure notifications:
```mermaid theme={null}
flowchart TD
START(["I want notifications"]) --> Q1{Which channel?}
Q1 -->|Slack| Q2{Same channel for all runs?}
Q1 -->|Email| Q_EMAIL{Same recipients every run?}
Q1 -->|Teams| Q_TEAMS{Same webhook behavior every run?}
Q2 -->|"Yes - project default"| SLACK_PROJECT["Project Settings"]
Q2 -->|"No - varies by test plan"| SLACK_PLAN["Test Plan Settings"]
Q2 -->|"No - varies per run/PR"| SLACK_API["Per-Run Override"]
Q_EMAIL -->|Yes| EMAIL["Test Plan Settings"]
Q_EMAIL -->|No - per run| EMAIL_API["Per-Run Override"]
Q_TEAMS -->|Yes| TEAMS["Test Plan or Project Settings"]
Q_TEAMS -->|No - per run| TEAMS_API["Per-Run Override"]
EMAIL --> NOTE1["Add recipients"]
EMAIL_API --> NOTE1B["Pass email in notifications array"]
TEAMS --> NOTE2["Configure webhook URL"]
TEAMS_API --> NOTE2B["Pass teams notifyOn or enabled false"]
SLACK_PROJECT --> NOTE3["Set default channel"]
SLACK_PLAN --> NOTE4["Override channel per plan"]
SLACK_API --> NOTE5["Pass notifications array in API call"]
```
| I want to... | Configure at |
| ---------------------------------------------- | --------------------------------------- |
| Set default Slack channel for all runs | Project Settings → Integrations → Slack |
| Add email recipients for a test plan | Test Plan → Notification Settings |
| Send to different Slack channels per test plan | Test Plan → Notification Settings |
| Send to different Slack channels per PR/run | Per-Run API / Run with Options |
| Override email recipients for one run | Per-Run API / Run with Options |
| Change Teams notifyOn or disable for one run | Per-Run API / Run with Options |
| Set up Microsoft Teams notifications | Project Settings or Test Plan Settings |
| Disable all notifications for one run | Per-Run API (`type: "none"`) / UI |
| Receive weekly summaries | Automatic for all org members |
## Setup by Channel
### Slack
Go to Organization Settings → Connections and connect your Slack workspace.
Go to Project Settings → Integrations → Slack and select a default channel.
Open a test plan → Click settings icon (gear) next to Notifications → Select
a different Slack channel.
The QA.tech bot must be invited to any channels you want to use.
### Email
Email notifications are configured per test plan - there is no project-level default.
Navigate to your test plan from the Test Plans page.
Click the settings icon (gear) next to **Notifications** on the test plan
page.
Search for organization members by name or email and add them as recipients.
Select **Every completion** (all runs) or **Only on failure** (failures
only).
In test plan settings, email recipients are selected from organization
members. Per-run API and Run with Options overrides accept any valid email
address.
### Microsoft Teams
Teams notifications use incoming webhooks. See [Microsoft Teams Integration](/integrations/microsoft-teams) for detailed webhook setup instructions.
Follow Microsoft's guide to create an incoming webhook for your Teams
channel.
Go to Project Settings → Integrations → Microsoft Teams, or configure at the
test plan level via Notification Settings.
Choose **Every completion** or **Only on failure** in test plan settings.
## Advanced Configuration
### Per-Test-Plan Settings
Access test plan notification settings by clicking the settings icon (gear) next to **Notifications** on any test plan page. You can configure:
| Setting | Description |
| ---------------- | ------------------------------------------- |
| Slack channel | Override project default for this test plan |
| Email recipients | Add organization members to receive emails |
| Email frequency | Every completion or only on failure |
| Teams webhook | Add Teams webhook URL for this test plan |
| Teams frequency | Every completion or only on failure |
Each test plan can have different notification settings, allowing you to:
* Route critical test plans to dedicated alert channels
* Add stakeholders to specific test plans
* Use different Teams channels for different test plans
### Per-Run Overrides
When triggering runs via the [Start Run API](/api-reference/runs/start-test-run) or **Run with Options** in the UI, you can override notifications for that specific run.
```json theme={null}
{
"testPlanShortId": "abc123",
"notifications": [
{ "type": "slack", "channel": "C0478ABCDEF", "notifyOn": "always" },
{
"type": "email",
"recipients": ["qa@example.com"],
"notifyOn": "failure"
},
{ "type": "teams", "notifyOn": "failure" }
]
}
```
Suppress all notifications (including project defaults):
```json theme={null}
{
"testPlanShortId": "abc123",
"notifications": [{ "type": "none" }]
}
```
**Key points**:
| Behavior | Description |
| --------------------- | -------------------------------------------------------------------------- |
| Slack channel format | Use Slack channel ID (e.g., `C0478ABCDEF`), not channel name |
| Email recipients | Any valid email addresses; replaces test-plan email settings for the run |
| Teams | Uses the project Teams webhook; set `notifyOn` or `enabled: false` |
| `type: "none"` | Suppresses Slack, email, and Teams for the run, including project defaults |
| `notifyOn: "always"` | Receive start (Slack) and finish notifications (default) |
| `notifyOn: "failure"` | Receive finish notifications only when result is not `PASSED` |
| Bot access | QA.tech Slack bot must be invited to specified channels |
For complete API documentation, see [Start Run API Reference](/api-reference/runs/start-test-run).
# Dependency Output States
Source: https://docs.qa.tech/core-concepts/output-state-optimization
Speed up test execution by reusing browser state from recently completed dependency tests
When you have tests with ["Resume From" dependencies](/core-concepts/dependencies#resume-from) (like a "Login" test that other tests rely on), QA.tech can automatically:
* **Skip running dependency tests** that completed successfully in the last 6 hours
* **Reuse the browser state** (login session, cookies, etc.) and output data from the previous run
* **Start your main tests immediately** with the correct browser state already loaded
This dramatically reduces test execution time while maintaining the same reliability.
## How It Works
### Example Scenario
Let's say you have these tests:
1. **Login Test** (creates a logged-in user session)
2. **Create Order Test** (resumes from Login Test)
3. **View Dashboard Test** (resumes from Login Test)
**Without optimization:**
* Login Test runs first
* Create Order Test waits for Login Test to complete, then runs
* View Dashboard Test waits for Login Test to complete, then runs
**With dependency output states enabled:**
* Login Test was already run successfully earlier
* Create Order Test starts immediately with saved login session
* View Dashboard Test starts immediately with saved login session
* **Result: Significantly faster execution**
## When Dependency Output States are Used
QA.tech automatically uses dependency output states for **"Resume From"** dependencies when:
✅ **Dependency test completed successfully** within the last 6 hours\
✅ **Same environment** (dependencies must match the test environment)\
✅ **Browser state is valid** (login sessions haven't expired)
If any condition isn't met, the dependency test runs first to generate fresh browser state.
**Note**: "Wait For" dependencies always run fresh and do not use output state optimization.
## Benefits & Safety
### Faster Execution
Skip redundant dependency tests that recently completed successfully.
### Safe & Reliable
Multiple safety checks ensure reliability is never compromised:
* **Time Limits**: Only reuses browser state less than 6 hours old
* **Success Verification**: Only uses state from tests that passed completely
* **Graceful Fallback**: Runs tests normally if dependency output states aren't available
### Environment Aware
**Environment Isolation**: Never mixes browser state between different environments - each environment's dependencies are kept separate.
## Common Questions
### Will this affect my test results?
No. Dependency output states only skip tests when we're certain the browser state is identical to what would be achieved by running the dependency. If there's any doubt, the system runs tests normally.
### What if my dependency test fails?
If any [dependency test](/core-concepts/dependencies) fails, all dependent tests will be skipped in the current test run. Dependency output states are only used for "Resume From" dependencies that completed successfully.
### What types of browser state are preserved?
The system preserves:
* Login sessions and authentication cookies
* Local storage data
* Session storage data
* Any other browser state your tests create
## Troubleshooting
### My tests are running slower than expected
* Verify your dependency tests completed successfully in the last 6 hours
* Ensure you're running tests in the same environment as recent dependency runs
### Dependency output states aren't being used
* Confirm dependency tests completed successfully within the last 6 hours
* Check that you're testing in the same environment where dependencies ran
### My test is using stale output data (old OTP codes, expired tokens)
Output data from dependency tests (extracted values, OTP codes, tokens) is preserved along with browser state for up to 6 hours. For time-sensitive data, click **"Run w. Dependencies"** to force fresh runs of all dependency tests.
# Ownership
Source: https://docs.qa.tech/core-concepts/ownership
Assign team members to test cases and test plans to organize responsibilities and track your work
Test ownership lets you assign team members to specific test cases and test plans. This helps organize testing responsibilities, track your work through a dedicated dashboard, and filter tests by owner.
Ownership is optional - tests can remain unassigned or be assigned to any organization member.
## Assigning Owners
### To Test Cases
Navigate to the test case you want to assign and click to open the edit
page.
In the sidebar, click the **Settings** tab, then expand the **Advanced**
section.
In the **Owner** section, select a team member from the dropdown. Choose "No
owner" to remove an existing assignment.
Save the test case to apply the ownership change.
Ownership is saved with each test case revision. When you update a test case,
the owner assignment carries forward to the new revision.
### To Test Plans
You can assign an owner when creating a test plan or update it afterward:
**During creation:** In the "Add Test Plan" modal, select an owner from the optional "Owner" field.
**After creation:**
Navigate to the test plan from the Test Plans page.
Click the **Settings** tab within the test plan.
In the **Owner** section, select a team member or choose "No owner" to
remove the assignment.
### In the Add Test Case Modal
When adding a test case manually using the "Add test case" modal, expand the **Advanced** section to find the Owner field.
### Where to Assign
| Where | When to Use | Notes |
| ------------------- | ------------------------------- | --------------------------------------- |
| Test case editor | Setting owner for existing test | In Settings tab, under Advanced section |
| Test plan creation | Assigning owner during setup | Optional field in creation modal |
| Test plan settings | Changing owner after creation | Update in Settings tab |
| Add test case modal | Setting owner during creation | In Advanced section of the modal |
## My items Dashboard
The **My items** tab on your project dashboard provides a focused view of everything you own.
**Location:** Project dashboard → **My items** tab
| Widget | Shows |
| -------------------- | --------------------------------------------------------------- |
| Test execution trend | Pass/fail trend chart for your owned tests over time |
| Recent failures | Up to 5 recent failures with direct links to results |
| My test plans | List of test plans you own (up to 50) with run status |
| My test cases | List of test cases you own (up to 10) with enabled/draft badges |
Each widget links directly to the relevant test or result page, making it easy to investigate issues or review your tests.
When you don't own any tests or test plans, the dashboard shows empty states
indicating where your owned items will appear.
## Filtering by Owner
On the test cases page, you can filter to show only tests you own:
1. Click the **Filters** dropdown above the test list
2. Check the **Owned by me** checkbox
3. The list updates to show only test cases where you are the owner
This filter combines with other filters (status, applications, etc.) and persists across sessions.
Filtering by owner is useful during sprint planning to focus on your team's
tests, or when reviewing tests you're responsible for maintaining.
## Common Patterns
### Feature Team Ownership
**Scenario:** A team owns specific feature areas.
Assign test cases to the team member responsible for that feature. Use the "My items" dashboard to track your feature's test health and filter by owner during standups.
### Developer-Owned Tests
**Scenario:** Developers create and maintain tests for their features.
Assign ownership when creating tests via [AI Chat](/core-concepts/ai-chat-assistant) or the test modal. Developers can use "Owned by me" filter to review their tests before deployments.
### QA Team Organization
**Scenario:** QA team distributes test maintenance across members.
Assign test plans to QA engineers. Use the execution trend chart in "My items" to identify tests needing attention and track pass rates over time.
## Best Practices
1. **Assign Clear Ownership**:
* Set owners when responsibilities are defined
* Update ownership when team structure changes
* Leave tests unowned if ownership is unclear
2. **Use the Dashboard Effectively**:
* Review "My items" regularly to track your testing workload
* Act on recent failures shown in the widget
* Monitor execution trends to identify flaky tests
3. **Combine with Other Features**:
* Use owner filtering during sprint planning
* Assign owners to [test plans](/core-concepts/test-plans) for organized test management
* Filter by owner when reviewing test results
Ownership works best when it reflects actual team structure. If you're unsure
who should own a test, leave it unassigned until responsibilities are clear.
# Parallel Test Execution and Concurrency Limits
Source: https://docs.qa.tech/core-concepts/parallel-test-execution
Control how many QA.tech test cases run in parallel. Set per-environment concurrency limits to protect staging servers or maximize throughput on production.
QA.tech runs independent test cases in parallel so test plans finish faster than running tests one at a time. By default, there is no cap on how many tests can run at once. You can set a **Maximum Concurrent Tests** limit on each environment when you need to protect shared infrastructure or control load on a target URL.
## How Parallel Execution Works
When you trigger a test plan or multi-test run, QA.tech schedules tests based on your [dependency graph](/core-concepts/dependencies):
| Test relationship | Execution behavior |
| ----------------------------------- | ---------------------------------------------------------------------- |
| Independent tests (no dependencies) | Run in parallel, each in its own isolated browser session |
| Tests in the same dependency chain | Run sequentially within the chain |
| Tests linked by Wait For | Dependent test waits for output data; chains can still run in parallel |
With no concurrency limit configured, QA.tech auto-scales to run many tests at the same time. A test plan with 20 independent three-minute tests can finish in roughly three minutes instead of an hour.
Parallel execution respects [test dependencies](/core-concepts/dependencies).
Limits control how many tests run at the same time within a single run; they
do not change dependency order.
## Configure in the Dashboard
Concurrency limits are configured **per environment**, not per organization or project. Leave **Maximum Concurrent Tests** empty for unlimited parallel runs, or enter a positive number to cap concurrency for that environment.
Go to [**Settings → Applications &
Envs**](https://app.qa.tech/current-project/settings/applications) and
select the application.
Scroll to **Environments**, open the hamburger menu on the environment you
want to change, and click **Edit**.
Leave the field empty for unlimited parallel runs, or enter a positive
number to limit concurrency.
Click **Save** to apply the limit to future runs that use that environment.
## How Limits Apply During a Run
When a run starts, QA.tech reads the limit from each environment used by tests in that run and applies the **most restrictive** value:
```
Run uses Environment A (limit: 10) and Environment B (limit: 5)
→ Effective limit for the run: 5
```
| Scenario | Effective limit |
| -------------------------------------------- | ------------------------- |
| All environments unlimited | No cap; full auto-scaling |
| One environment set to `5`, others unlimited | `5` |
| Environment A: `10`, Environment B: `3` | `3` (minimum wins) |
Tests that exceed the limit are **queued**, not skipped. QA.tech delays extra tests and starts them as slots open, so the full test plan still completes.
## When to Set a Limit
Use **Maximum Concurrent Tests** when parallel runs could overwhelm the environment you are testing:
| Situation | Suggested approach |
| ------------------------------------------- | ----------------------------------------------------------------------- |
| Shared staging server with limited capacity | Set a low limit (for example `3` to `5`) on the staging environment |
| Production smoke tests on a live site | Use a conservative limit to avoid traffic spikes |
| Dedicated preview or load-test environment | Leave unlimited or set a high limit |
| CI runs against ephemeral preview URLs | Often unlimited; set a limit if your preview host throttles connections |
Leave the field **empty** when you want the fastest possible test plan execution and your infrastructure can handle concurrent load.
## Maximize Parallelism Without Overloading
Parallel execution speed depends on how tests are structured:
1. **Reduce unnecessary dependencies** - Independent tests run at the same time; long dependency chains run one after another. See [Test Dependencies](/core-concepts/dependencies).
2. **Set limits per environment** - Use a lower limit on staging and a higher or unlimited limit on dedicated test environments.
3. **Use Resume From for shared login** - Reuse browser state within a chain instead of creating many parallel login tests against the same auth endpoint.
For test plan execution patterns and scheduling, see [Test Plans](/core-concepts/test-plans) and [Running Tests](/best-practices/running-tests).
## Frequently Asked Questions
No. Limits are configured per **environment** under **Settings → Applications
& Envs**. If you need the same cap everywhere, set it on each environment
(staging, production, and so on).
No. Up to five tests run **at the same time** for that run. When one finishes,
the next queued test starts. The full plan still runs to completion.
Each **active test** uses a slot. Tests in the same chain run one after
another, so they typically use one slot at a time for that chain. Independent
chains each use their own slots up to your limit.
Not today. **Maximum Concurrent Tests** is configured in the dashboard on each
environment. Per-run API overrides support environment URLs and device
presets; see [Start Run API](/api-reference/runs/start-test-run).
The environment has no concurrency cap. QA.tech parallelizes independent tests
and scales concurrent agents based on demand.
## Related Documentation
* [Projects, Applications, Environments](/core-concepts/applications-and-environments) - Where environments and URLs are defined
* [Test Plans](/core-concepts/test-plans) - Group tests and trigger parallel runs
* [Test Dependencies](/core-concepts/dependencies) - Control order and browser isolation
* [Running Tests](/best-practices/running-tests) - Trigger runs from the UI, CI/CD, or schedules
# Roles and Permissions
Source: https://docs.qa.tech/core-concepts/roles-and-permissions
Role-based access in QA.tech
# Organization roles and permissions
Your **role** is set per **organization**. The same person can be an Owner in one organization and a Member in another.
This page matches how the QA.tech app enforces access for billing, team management, agreements, and certain integrations. Other product areas may use the same role where noted below.
## Organization Owner, Admin, and Member
| Role | In the product (short) |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| **Owner** | Can change settings, invite people, and manage billing. |
| **Admin** | Full product access, can invite people, and manage billing. |
| **Member** | Can use the product for testing and related work, but not billing or team management (see the [matrix below](#what-each-role-can-do)). |
## What each role can do
| Capability | Member | Admin | Owner |
| --------------------------------------------------------------------------------------- | ------ | ----------------------------------------------- | --------------------------------------------------- |
| **Projects, tests, and day-to-day product use** | Yes | Yes | Yes |
| **Billing and subscription** (including purchase and plan changes) | No | Yes | Yes |
| **Accept organization terms of use** (where the app requires it) | No | Yes | Yes |
| **Invite users** to the organization | No | Yes | Yes |
| **Change another user’s role** or **remove** them from the organization (team settings) | No | Yes, for **Members and Admins** (not the Owner) | Yes, for **Members and Admins** (not another Owner) |
| **Transfer ownership** (hand off Owner to someone else) | No | No | Yes |
| **Non-public organization integrations** (connectors that are not shown to all members) | No | Yes | Yes |
**Notes:**
* An **Admin** can manage other **Admins** and **Members**, but cannot use actions that only apply to the **Owner** (for example, they cannot change the Owner’s role, remove the Owner, or transfer ownership from the Owner).
* **Transfer ownership** is only available to the current **Owner**. Ownership is not assigned when sending a normal email invite. If you need a new Owner, a current Owner uses **Transfer ownership** for an existing member.
## Inviting people and choosing a role
Only **Admins** and **Owners** can send invitations.
When you choose a role for an invite:
* You can assign a role that is the **same** as yours or **lower** (for example, an Admin can invite an Admin or a Member; an Owner can invite an Admin or a Member).
* You **cannot** choose **Owner** on an invite. The **Owner** role is only granted when someone creates the organization, or when ownership is **transferred** to an existing member.
## Why you might not see a button or section
The app hides or disables actions you are not allowed to perform (for example, billing and invite flows for **Members**, or the **Transfer ownership** action when you are not the **Owner**). If you need to do something that is restricted, ask an **Admin** or the **Owner** of your organization.
# Session State Lifetime
Source: https://docs.qa.tech/core-concepts/session-state-lifetime
Control how long saved browser states from dependency tests remain valid before requiring a fresh run
Session State Lifetime controls how long [dependency output states](/core-concepts/output-state-optimization) are considered valid. When a test with a ["Resume From" dependency](/core-concepts/dependencies#resume-from) completes successfully, QA.tech captures the browser session state — cookies, local storage, and indexed DB — at the end of the test. Other tests that depend on it can then resume from that saved state instead of re-running the dependency.
Once the Session State Lifetime expires, the saved state is no longer reused. The dependency test runs again from scratch to produce a fresh state.
The default lifetime is **6 hours**, which works well for most applications.
You only need to change this if your tests are failing due to expired sessions
or if you want to optimize execution time for long-lived sessions.
## Why Change the Default?
The right lifetime depends on how long your application's sessions stay valid.
**If your sessions are shorter than 6 hours**, tests resuming from an old output state will encounter expired sessions and fail. Lowering the lifetime to match your token expiry prevents this.
**If your sessions are longer than 6 hours**, you can increase the lifetime to reduce how often dependency tests re-run, saving execution time.
### Common Scenarios
| Application Type | Typical Session Duration | Recommended Lifetime |
| :--------------------------------------------- | :----------------------- | :------------------- |
| Banking / financial apps | 15–30 minutes | 15 or 30 minutes |
| Apps with short-lived JWT tokens | 1–2 hours | 1–2 hours |
| Standard web applications | 4–8 hours | 6 hours (default) |
| Apps with "remember me" or long-lived sessions | 12–24+ hours | 12–24 hours |
## How to Configure
Go to **Settings → Applications & Envs**, then select the application you
want to configure and click **Edit**.
Find the **Session State Lifetime** dropdown and choose a value between 5
minutes and 7 days.
Click **Update Application**. The change takes effect immediately for all
tests under that application.
Existing output states older than the new lifetime are immediately considered
expired. If you lower the lifetime, dependency tests that were previously
skipped may need to run again on the next execution.
## How It Affects Test Execution
When QA.tech runs a test with dependencies, it checks whether a valid (non-expired) output state exists for each "Resume From" dependency:
* **Valid state exists** — the dependency test is skipped and the saved browser state is used directly
* **No valid state** (expired or never created) — the dependency test runs first to produce a fresh state
This means:
* **Lowering the lifetime** → dependency tests run more frequently. More reliable when sessions expire quickly, but increases total execution time.
* **Raising the lifetime** → dependency tests are skipped more often. Faster execution, but risks resuming from a stale session if your application's tokens expire before the lifetime does.
## Important Notes
* **Per-application setting** — different applications in the same project can have different lifetimes.
* **Retroactive** — changing the lifetime immediately affects whether existing output states are considered valid.
* **Explicit invalidation** — output states can be [manually invalidated](/core-concepts/output-state-optimization) regardless of the lifetime setting, for example by clicking "Run w. Dependencies" to force a fresh run.
* **Same-run trust** — when a test is re-run within the same run (e.g., retrying failed tests), output states produced during that run are always trusted regardless of the lifetime setting.
* **Manual overrides** — output states explicitly selected in the test editor bypass the lifetime check.
# Shared Steps
Source: https://docs.qa.tech/core-concepts/shared-steps
Reuse the same sequence of steps across multiple tests using dependencies, agent knowledge, or assistant docs
When several tests start with the same sequence (log in, navigate to a page, set up data), you don't have to rewrite those steps in every test. QA.tech gives you three ways to share steps, each with different trade-offs. Pick based on whether the steps should **run once and be reused**, **run fresh every time**, or be **copied into each test**.
## Quick comparison
| Approach | Steps run... | Best for | Not good for |
| -------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------------------------- |
| **Dependencies** | Once, then reused | Login, navigation, expensive setup you don't want to repeat | Creating multiple new things (steps only run once) |
| **Agent knowledge** (Agent Rule) | Fresh on every test | Repeated procedures the chat struggles to generate, including creating new things | One-off steps; long procedures consulted on every action |
| **Assistant docs** (Library knowledge) | Copied into each test at creation | Self-contained tests you want to edit per-test | Keeping many tests in sync (copies are static) |
## Option 1: Dependencies
Put the shared steps in their own test, then have other tests depend on it. With **Resume From**, dependent tests inherit the browser state (login session, cookies) so the steps don't run again. See [Test Dependencies](/core-concepts/dependencies) for Resume From vs. Wait For.
**Use when:** the steps establish state you want to reuse and **don't** want to repeat - logging in, navigating to a starting page, or expensive one-time setup.
**Avoid when:** each dependent test needs the steps to actually run again - for example creating a new record per test. Resume From runs the parent once and reuses its end state, so it can't create a fresh thing for every child.
Keep tests to 10 steps or less. If a workflow is longer, split it and chain
with a dependency instead of one giant test.
**Pros:** real shared session and state, no repeated work, fast for large suites that share a login.
**Cons:** dependent chains run sequentially; each test has a single Resume From parent; steps execute only once.
## Option 2: Shared steps as agent knowledge
When the chat has trouble generating a tricky sequence reliably, or you need the steps to **run fresh every time** (like creating a new entity), write them once as an **Agent Rule** under [**Settings → Knowledge → Test Agent**](/core-concepts/knowledge#test-agent-tab). The test agent reads matching rules during execution, so you can reference the procedure by name in a test step instead of spelling it out each time.
### Write the steps in clear LLM-friendly syntax
Give the rule a short, unique title and a numbered procedure:
```md theme={null}
## Shared steps: Create a project
Use these steps whenever a test needs a new project.
1. Click "New project" in the top navigation.
2. Enter a unique project name.
3. Select the "Blank" template.
4. Click "Create" and wait for the project dashboard to load.
```
### Reference it from a test step
In the test, point at the rule by its title rather than repeating the steps:
> Follow the "Create a project" shared steps, then open Settings and rename the project.
### Filter so it only loads where needed
Agent Rules support targeting filters - **application, labels, scenario, or URL path**. Set a scenario or URL path filter so the procedure is only loaded for tests that need it and doesn't add noise to unrelated tests.
| Filter | Example | Effect |
| -------- | ----------- | -------------------------------------------------- |
| Scenario | `Projects` | Loaded only for tests in the Projects scenario |
| URL path | `/projects` | Loaded only when the test starts under `/projects` |
**Pros:** runs fresh every time (works for creating new things), one source of truth referenced by name, targeting keeps it out of unrelated tests.
**Cons:** Agent Rules are consulted on every relevant action, so keep procedures concise; it is guidance the agent follows, not a guaranteed macro.
## Option 3: Shared steps as assistant docs
If you'd rather the steps live **inside each test** as normal, editable steps, add them as a text item in your [Knowledge Library](/core-concepts/knowledge#library) (or an Assistant Rule). The [AI Chat Assistant](/core-concepts/ai-chat-assistant) searches your knowledge when creating or editing tests and **copies the steps into the test definition**.
> Use the "Create a project" steps from my knowledge to start this test, then verify the dashboard loads.
**Pros:** each test is self-contained and individually editable; no runtime dependency or rule needed.
**Cons:** the copied steps are a static snapshot - updating the knowledge item does **not** update tests already created from it. Re-ask the assistant to re-apply if the procedure changes.
## Which should I use?
* **Login, navigation, or setup you don't want to repeat** → Dependencies (Resume From).
* **A procedure the agent must run every time (e.g. create a new record) or struggles to generate** → Agent knowledge, filtered by scenario or route.
* **You want the steps written into each test so they're self-contained and editable** → Assistant docs (accepting they're static copies).
# Test plans
Source: https://docs.qa.tech/core-concepts/test-plans
Test Plans allow you to organize and manage groups of test cases that can be executed together as a single unit.
## Overview
Test Plans are collections of test cases that you want to run together regularly. This feature provides flexibility in running tests through multiple methods: API triggers, scheduled runs, or manual execution through the UI.
Test plans help streamline your testing process by:
* Organizing related test cases into logical groups
* Enabling automated execution on schedules
* Supporting API-driven test automation
* Managing test dependencies efficiently
## Creating a Test Plan
1. Navigate to the Test Plans section
2. Click the "Create Test Plan" button in the top right
3. Provide the following information:
* Name: A clear, descriptive name for your test plan
* Description (optional): Additional context about the test plan's purpose
* Owner (optional): Assign a team member responsible for this test plan. See [Ownership](/core-concepts/ownership)
4. Click "Save" to create your test plan
## Adding Test Cases to a Test Plan
When adding test cases to a test plan, keep in mind:
* Dependencies will be automatically included in runs, even if not visible in the UI
* Consider creating separate plans for:
* Frequently run core tests
* Less frequent tests that run on a schedule
## Execution Methods
### CI/CD Integration
Test Plans are the primary way to run tests from CI/CD pipelines. You can trigger test plans programmatically via the QA.tech REST API from any CI/CD system. See [CI/CD Integration](/configuration/ci-cd-integration) for an overview of integration options and platform-specific guides.
**How it works:**
1. Create a test plan and add test cases to it
2. Get the test plan's **Test Plan Short ID** ([prefixed ID](/configuration/gitlab#finding-your-test-plan-short-id) visible in the test plan URL)
3. Trigger the test plan via API from your CI/CD pipeline
**Should work with any CI/CD system** - Since QA.tech uses a standard REST API, you can integrate from any CI/CD platform that can make HTTP requests (using curl, HTTP libraries, or built-in HTTP steps). Examples include:
* GitLab CI/CD
* GitHub Actions
* Bitbucket Pipelines
* Azure DevOps
* CircleCI
* Jenkins
* Any other CI/CD system with HTTP request capabilities
### GitHub Actions Integration
You can automate test execution by integrating Test Plans with your GitHub workflows. This integration allows you to:
* Trigger tests automatically after deployments
* Run tests on pull requests
* Execute tests on any GitHub event
Read more about setting up [GitHub Actions](/configuration/github-actions).
### API Execution
Test Plans can be triggered programmatically through the API. For detailed information about available endpoints and parameters, refer to the [API reference documentation](/api-reference/runs/start-test-run).
**Example:**
```bash theme={null}
curl -X POST https://api.qa.tech/v1/run \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"testPlanShortId": "pln_abc123"
}'
```
The `testPlanShortId` is the [prefixed Test Plan Short ID](/configuration/gitlab#finding-your-test-plan-short-id) visible in your test plan settings (e.g., `pln_abc123`).
### Scheduled Execution
You can configure Test Plans to run automatically at specific times or intervals:
Go to [Test Plans](https://app.qa.tech/current-project/test-plans) in your
project dashboard.
Click on the test plan you want to schedule.
Click on **Manage Schedules** within the test plan.
Add your cron schedule expression and a description, then click **Add
Schedule**.
### UI Execution
To run a Test Plan manually:
1. Navigate to the Test Plans section
2. Locate your Test Plan
3. Click the "Run Tests" button
## Performance & Parallel Execution
QA.tech automatically runs tests in parallel to minimize total execution time when executing test plans:
* **Automatic parallelization**: Tests run concurrently by default when executing a test plan, significantly reducing the time required to complete a full test suite
* **Dependency-aware**: Parallel execution respects the dependency graph, ensuring dependent tests run in the correct order while maximizing parallelization opportunities
* **Configurable limits**: Set **Maximum Concurrent Tests** on each [environment](/core-concepts/applications-and-environments#maximum-concurrent-tests) to cap parallel runs against staging, production, or other URLs
* **Auto-scaling**: When no limit is set, the system scales up to approximately 100 concurrent agents based on demand
* **Intelligent scheduling**: Independent tests execute simultaneously while dependent tests wait for their prerequisites to complete
This means that while a single test case may take several minutes to execute, running a test plan with multiple independent test cases will complete much faster than running them sequentially. For example, 20 independent tests that each take 3 minutes would complete in approximately 3 minutes when run as a test plan, rather than 60 minutes if run sequentially.
To maximize parallel execution benefits, organize your test cases to minimize
dependencies between tests. The more independent tests in your test plan, the
more parallelization can occur. To protect shared infrastructure, set a
per-environment concurrency limit. Learn more in [Parallel Test Execution and
Concurrency Limits](/core-concepts/parallel-test-execution), [Test
Dependencies](/core-concepts/dependencies), and [Creating
Tests](/best-practices/creating-tests).
## Best Practices
1. **Optimize Test Organization**:
* Group related test cases together
* Create separate plans for different testing frequencies
* Keep plans focused and maintainable
2. **Dependency Management**:
* Be aware that dependent test cases will be included automatically
* Review dependencies when setting up new test plans
3. **Schedule and Workflow Optimization**:
* Schedule less frequent tests during off-peak hours
* Configure GitHub workflows to run tests at key points in your development process (deployments, PRs, merges)
4. **Managing Different Versions and Releases**:
* Use Test Scenarios to define the specific test cases to run for a given version or release
* The same Test Plan can be reused across different environments by overriding environment URLs via API (see [Start Run API](/api-reference/runs/start-test-run)) or updating test plan parameters in the UI
* **Note:** Both environment and device preset overrides are supported via API. See [Start Run API](/api-reference/runs/start-test-run) for details on using `applications` overrides.
5. **Notification Configuration**:
* Configure email, Slack, and Microsoft Teams notifications at the test plan level
* Override the project's default Slack channel for test plan-specific routing
* Add organization members as email recipients to ensure stakeholders receive run completion notifications
* See [Notifications](/core-concepts/notifications) for detailed setup instructions
## Test Plan Parameters
Test plan parameters let you configure environment and device preset settings per application. When a test plan runs, each test uses the environment configured for its application.
### Configuration Levels
Parameters follow a precedence hierarchy where later settings override earlier ones:
```
Project Settings (defaults for all runs)
└── Test Plan Parameters (override per test plan)
└── Per-Run API (override per individual run)
```
### Configuring Parameters in the UI
Navigate to your test plan and expand the **Parameters** section. You'll see a card for each application that has test cases in the plan.
| Setting | How to Configure | Can Clear? |
| ----------------- | ----------------------------------------------------------------- | ---------------------- |
| **Environment** | Select from dropdown | No - always required |
| **Device Preset** | Select from dropdown, or choose "Default" to use project defaults | Yes - select "Default" |
Applications only appear in the Parameters section if they have test cases
included in the test plan.
### How Tests Execute with Multiple Applications
Each test case belongs to **one application** and runs **once** using that application's configured environment. Tests do not run across multiple applications.
**Example:**
* Test Plan contains: Test A (App 1), Test B (App 1), Test C (App 2)
* Parameters: App 1 → Production, App 2 → Staging
* Execution:
* Test A runs on Production (App 1's environment)
* Test B runs on Production (App 1's environment)
* Test C runs on Staging (App 2's environment)
This means different tests in the same test plan can run against different environments based on their application assignment.
### API Overrides
You can override parameters at runtime via the [Start Run API](/api-reference/runs/start-test-run). API overrides take highest precedence:
```json theme={null}
{
"testPlanShortId": "pln_abc123",
"applications": [
{
"applicationShortId": "app_gXeBl2",
"environment": { "url": "https://preview.example.com" },
"devicePresetShortId": "preset_abc123"
}
]
}
```
See [Start Run API](/api-reference/runs/start-test-run) for full details on the `applications` override format.
# Tests and Results
Source: https://docs.qa.tech/core-concepts/tests-and-results
Scenarios are groups of test cases that belong together
## Scenarios
Scenarios are groups of test cases that belong together. They are best organized around user flows, such as:
* Login
* Product Search
* Add to shopping cart
* Checkout
* Notification Settings
Each scenario should optimally include both positive and negative test cases. For example, a "Checkout Process" scenario might include:
* Successful purchase with valid payment details
* Order completion with fedex shipping
* Payment attempt with expired credit card (negative case)
* Checkout with empty cart (negative case)
This structure helps ensure comprehensive testing of each user flow, including both expected successes and potential failure points.
### Negative Testing
Negative testing is a QA practice that validates how a system handles invalid inputs, unexpected user behavior, and error conditions. Think of it as "testing what shouldn't work" to ensure the application fails gracefully and securely.
* Validating error messages
* Ensuring system stability under incorrect usage
* Preventing security vulnerabilities
* Maintaining data integrity
## Tests and Test Runs
Every time a test run is triggered, we run the selected scenarios. The result is a test run with one or more test cases in it.
## Filtering and Searching Results
When viewing test run results, you can quickly find specific tests using filters and search:
* **Filter by result** - Show only PASSED, FAILED, or SKIPPED tests
* **Filter by status** - Filter tests by execution state (INITIATED, RUNNING, COMPLETED, ERROR, CANCELLED)
* **Search by name** - Find specific tests using the search box
* **Filter by owner** - Show only tests you own with the "Owned by me" filter. See [Ownership](/core-concepts/ownership)
**Result vs. Status**: A test's *result* is the outcome after completion
(PASSED/FAILED/SKIPPED), while *status* tracks the execution state. For
example, a test can have STATUS=COMPLETED with RESULT=FAILED.
All filters persist in the URL, making it easy to share specific views with your team.
# Time Saved
Source: https://docs.qa.tech/core-concepts/time-saved
## What is Time Saved
We refer to "Time Saved" throughout the application. This is a number of minutes that you would have saved if you were to perform all these tests with human QA resources.
## Source
We ran a test using a freelancer service to hire QA testers to perform the same tests as our agent did. Read the full blog post about it at [https://qa.tech/blog/qa-testing\_humans-vs-ai/](https://qa.tech/blog/qa-testing_humans-vs-ai/)
# POC Mobile Setup Checklist
Source: https://docs.qa.tech/getting-started/poc-mobile-setup-checklist
Prepare native iOS and Android apps before a Proof of Concept with QA.tech
Before a mobile Proof of Concept (POC) kickoff, verify the items below are in place. QA.tech uploads your build, configures the project, and runs the same checks during setup — app launch, backend connectivity, and login — before you start creating tests.
See the [Web POC Setup Checklist](/getting-started/poc-setup-checklist) for
staging URLs, email whitelisting, and browser authentication.
***
## Before kickoff — verify these are ready
| Your team verifies | QA.tech verifies during setup |
| -------------------------------------------------- | --------------------------------------- |
| Mobile testing enabled for your org | Mobile application created |
| Backend mobile IPs allowlisted | App reaches staging API |
| Simulator `.app` or `.apk` builds and runs locally | Build uploaded and launches on emulator |
| Test accounts log in on staging | Login smoke test passes |
***
## What to verify, and effort if not ready
Work through these in order. Backend IP allowlisting usually takes the longest when IT is involved.
| Priority | Verify this is in order | Effort if not ready | Details |
| -------- | --------------------------------------------------- | ------------------- | --------------------------------------------------- |
| 🔴 **1** | Mobile testing is enabled for your organization | Minutes | [Enable mobile testing](#1-enable-mobile-testing) |
| 🔴 **2** | Your backend allows QA.tech mobile testing IPs | 1–5 days (IT) | [Backend network access](#2-backend-network-access) |
| 🟠 **3** | Simulator `.app` (iOS) or `.apk` (Android) is ready | Hours | [App build](#3-app-build) |
| 🟡 **4** | Dedicated test accounts log in on staging | Hours | [Test accounts & auth](#4-test-accounts--auth) |
| 🟢 **5** | QA.tech project is configured with your build | Minutes | [QA.tech project setup](#5-qatech-project-setup) |
***
## 1. Enable mobile testing
Mobile testing is enabled per organization. Contact [support](mailto:hi@qa.tech) or your QA.tech contact to enable it before the POC.
***
## 2. Backend network access
**Biggest blocker for mobile.** Your app may install and launch fine, but
tests fail with network errors or login failures if your API backend blocks
QA.tech traffic.
Mobile tests run on cloud emulators. API calls from the app exit through a **separate set of IP ranges** from web testing.
### Mobile testing IPs
Get the current list from [**Settings → Network**](https://app.qa.tech/current-project/settings/network) — see the **Mobile Testing IP Whitelist** section. Mobile testing uses different IP ranges than web testing.
IP addresses can change. Always use the live **Mobile Testing IP Whitelist**
in Settings — never rely on a static copy.
Add those ranges wherever your mobile backend enforces access:
* Backend firewall or security group (AWS, GCP, Azure)
* API gateway allowlist or rate-limiting rules
* VPN / zero-trust gateway (Tailscale, Cloudflare Access, Zscaler)
**Forward to IT:** [Email pre-filled request to your IT team](mailto:?subject=Whitelist%20QA.tech%20mobile%20testing%20IPs\&body=Hi%2C%0A%0AWe%20need%20to%20whitelist%20QA.tech%27s%20mobile%20testing%20IP%20ranges%20so%20their%20automated%20testing%20can%20reach%20our%20mobile%20app%20backend%20during%20our%20POC.%0A%0ACopy%20the%20current%20mobile%20IP%20ranges%20from%20Settings%20%E2%86%92%20Network%20\(Mobile%20Testing%20IP%20Whitelist\)%3A%0Ahttps%3A%2F%2Fapp.qa.tech%2Fcurrent-project%2Fsettings%2Fnetwork%0A%0AAdd%20those%20CIDR%20ranges%20to%20our%20backend%20firewall%2FAPI%20gateway%20allowlist.%0A%0ADocs%3A%20https%3A%2F%2Fdocs.qa.tech%2Ftest-features%2Fmobile-app-testing%23network-access%0A%0AThanks)
Or send the request to your contact person at QA.tech, who can help coordinate with your IT team.
### Your team verifies
* The app can reach your staging API from outside your corporate network
* IT has applied the mobile IP allowlist to the correct backend endpoints
### QA.tech verifies during setup
* App launches on the emulator and loads past the splash screen
* Login and API-dependent flows complete without network errors
See [Mobile App Testing — Network Access](/test-features/mobile-app-testing#network-access) and [IP Access](/configuration/ip-access-control).
***
## 3. App build
Mobile testing requires a **simulator or emulator build** — not an App Store or Play Store distribution build.
Build for **iOS Simulator** and compress the `.app` as `.zip` or `.tar.gz`.
```bash theme={null}
xcodebuild -scheme '{scheme_name}' \
-sdk iphonesimulator \
-configuration Debug
```
Locate the `.app` in `build/Debug-iphonesimulator/`, then:
```bash theme={null}
zip -r AppName.zip AppName.app
```
Upload the `.zip` (max 4 GB). App Store `.ipa` files are not supported.
Build an **APK** — not an `.aab` bundle.
```bash theme={null}
./gradlew assembleDebug
```
Find the APK in `{module}/build/outputs/apk/`. Upload the `.apk` (max 4 GB).
To convert an `.aab` to `.apk`, use [bundletool](https://developer.android.com/tools/bundletool) with `--mode=universal`.
Tests run on cloud iOS Simulators and Android Emulators. Configure simulators
and emulators with [mobile device
presets](/test-features/device-presets#mobile-device-presets). Physical device
testing is coming soon.
Full build instructions: [Mobile App Testing — Preparing Your App Build](/test-features/mobile-app-testing#preparing-your-app-build).
### Your team verifies
* The build installs and launches on a local simulator/emulator
* The build points at your staging backend (not production)
***
## 4. Test accounts & auth
Same principles as web testing:
* Create dedicated staging accounts for each role
* Verify login works on a local emulator before sharing credentials
* Share credentials with QA.tech for [Configs](/core-concepts/configs), or send them to your contact person at QA.tech who will add them for you
If your app uses email-based auth, also complete the [email whitelisting steps](/getting-started/poc-setup-checklist#3-email-delivery) from the web checklist.
***
## 5. QA.tech project setup
QA.tech handles this during onboarding:
[**Settings → Applications &
Envs**](https://app.qa.tech/current-project/settings/applications) → new
application → type **Mobile App**
Select **iOS** or **Android** and name the environment (for example,
Staging). No URL is required.
iOS: `.zip` / `.tar.gz` with simulator `.app` · Android: `.apk`
QA.tech verifies the app launches, can log in, and reaches your backend.
***
## Getting help
Contact [QA.tech Support](https://qa.tech/contact) or your QA.tech contact with your platform (iOS/Android), staging API URL, and how your backend is protected.
# POC Setup Checklist
Source: https://docs.qa.tech/getting-started/poc-setup-checklist
Prepare your web application before a Proof of Concept with QA.tech
Before a Proof of Concept (POC) kickoff, verify the items below are in place. QA.tech runs the same checks during project setup and will flag anything still blocking before you start creating tests.
Native iOS and Android POCs have separate requirements — APK/simulator builds,
mobile IP ranges, and backend allowlisting. See the [Mobile POC Setup
Checklist](/getting-started/poc-mobile-setup-checklist).
***
## Before kickoff — verify these are ready
| Your team verifies | QA.tech verifies during setup |
| ---------------------------------------------------- | -------------------------------------- |
| Staging URL loads and IT has applied IP allowlisting | Environment reachable from QA.tech |
| Test accounts log in on staging | Login test passes |
| `@qatech.email` is allowed and a test email arrives | Email inbox receives mail from staging |
| Test data is scrubbed and accounts are seeded | Crawl and initial smoke tests run |
| WAF/CAPTCHA bypasses are in place (if applicable) | Tests complete without bot blocks |
***
## What to verify, and effort if not ready
Work through these in order. Network access usually takes the longest when IT is involved.
| Priority | Verify this is in order | Effort if not ready | Details |
| -------- | --------------------------------------------------------- | ------------------- | ---------------------------------------------------- |
| 🔴 **1** | QA.tech can reach staging (IPs allowlisted or SSH tunnel) | 1–5 days (IT) | [Network access](#1-network-access) |
| 🔴 **2** | Dedicated test accounts exist and log in on staging | Hours | [Authentication](#2-authentication) |
| 🟠 **3** | `@qatech.email` is allowed and test emails arrive | Hours (IT) | [Email delivery](#3-email-delivery) |
| 🟡 **4** | Staging URL is shared and test data is safe to use | Hours–days | [Environment & test data](#4-environment--test-data) |
| 🟢 **5** | WAF, CAPTCHA, and deployment protection won't block tests | Hours (IT) | [WAF & bot protection](#5-waf--bot-protection) |
***
## 1. Network access
**Biggest blocker.** If QA.tech cannot reach your staging URL, nothing else
matters. Start here — especially if your app is behind a VPN, firewall, or IP
allowlist.
QA.tech browser tests exit through a fixed pool of outbound IPs. Your CDN, WAF, or firewall must allow traffic from these addresses.
### Web testing IPs
Get the current list from [**Settings → Network**](https://app.qa.tech/current-project/settings/network) or the [Get Outbound IPs API](https://api.qa.tech/v1/outbound-ips). Copy the addresses into your CDN, WAF, firewall, or IP allowlist.
IP addresses can change. Always use the live list from Settings or the API —
never rely on a static copy.
**Forward to IT:** [Email pre-filled request to your IT team](mailto:?subject=Whitelist%20QA.tech%20IPs%20for%20staging\&body=Hi%2C%0A%0AWe%20need%20to%20whitelist%20QA.tech%27s%20outbound%20IP%20addresses%20so%20their%20automated%20testing%20can%20access%20our%20staging%20environment%20during%20our%20POC.%0A%0ACopy%20the%20current%20IP%20list%20from%3A%0Ahttps%3A%2F%2Fapp.qa.tech%2Fcurrent-project%2Fsettings%2Fnetwork%0A%0AOr%20via%20API%3A%20https%3A%2F%2Fapi.qa.tech%2Fv1%2Foutbound-ips%0A%0AAdd%20those%20addresses%20to%20our%20CDN%2FWAF%2Ffirewall%20allowlist.%0A%0AQA.tech%20traffic%20identifies%20as%20QATechBot%20in%20the%20User-Agent.%0ADocs%3A%20https%3A%2F%2Fdocs.qa.tech%2Fconfiguration%2Fip-access-control%0A%0AThanks)
Or send the request to your contact person at QA.tech, who can help coordinate with your IT team.
### Private or VPN-protected environments
If staging is not on the public internet, set up an [SSH Tunnel Proxy](/configuration/ssh-tunnel) through a bastion host and whitelist QA.tech IPs on the bastion's SSH port.
### Your team verifies
* Staging URL loads in a browser from outside your office network (or via the jump server)
* IT has applied the IP allowlist and changes have propagated (allow 5–10 minutes for CDNs)
### QA.tech verifies during setup
* Environment is reachable from QA.tech infrastructure
* A simple navigation or login test completes without 403, timeout, or CAPTCHA errors
Platform-specific guides: [IP Access](/configuration/ip-access-control) · [SSH Tunnel](/configuration/ssh-tunnel) · [Cloudflare](/configuration/cloudflare-waf-turnstile)
***
## 2. Authentication
Dedicated test accounts let the AI agent log in without manual steps. Create them in staging, then share credentials with QA.tech for Configs.
One account per role you want to demonstrate (admin, standard user, etc.).
Use credentials that exist only in non-production environments.
Log in manually with each test account before the kickoff. Fix any account,
SSO, or rate-limit issues on your side first.
QA.tech adds them to [**Settings →
Configs**](https://app.qa.tech/current-project/settings/configs) during
project setup. Or send them to your contact person at QA.tech, who will add
them for you. See [Authentication](/best-practices/handle-auth) for 2FA,
OTP, and magic-link configs.
| Method | Extra preparation |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| **OTP / magic link** | Requires [email whitelisting](#3-email-delivery) |
| **2FA (TOTP)** | Provide the `otpauth://` URI from the QR code — see [2FA setup](/best-practices/handle-auth#two-factor-authentication-2fa) |
| **CAPTCHA** | Whitelist QA.tech IPs on staging to bypass |
| **SSO / SAML** | Test IdP, bypass route, or seeded session — coordinate with identity team |
| **BankID / national ID** | Stub in staging or see [SE BankID](/applications/se-bank-id) |
Config credentials are not encrypted and are passed to AI models during tests.
Never use production credentials.
***
## 3. Email delivery
Flows like signup verification, password reset, OTP, and magic links depend on your app delivering email to QA.tech inboxes (`@qatech.email`).
### Allow `@qatech.email`
Your IT team needs to allow the entire `@qatech.email` domain in signup restrictions, email gateways, and spam filters.
**Forward to IT:** [Email pre-filled request to your IT team](mailto:?subject=Allow%20%40qatech.email%20for%20QA.tech%20testing\&body=Hi%2C%0A%0AWe%20need%20to%20allow%20the%20%40qatech.email%20domain%20for%20our%20QA.tech%20POC.%0A%0APlease%20allow%20%40qatech.email%20in%3A%0A-%20Signup%20and%20registration%20restrictions%0A-%20Email%20server%20and%20gateway%20filters%0A-%20Spam%20and%20security%20tools%0A%0ADocs%3A%20https%3A%2F%2Fdocs.qa.tech%2Ftest-features%2Femail-inbox%23whitelisting-email-addresses%0A%0AThanks)
Or send the request to your contact person at QA.tech, who can help coordinate with your IT team.
### Your team verifies
1. Create a test account using a `@qatech.email` address (or ask QA.tech for the project address)
2. Trigger a verification or magic-link email from your app
3. Confirm the email arrives within a few minutes
### QA.tech verifies during setup
* Email inbox receives mail from your staging environment
* An email-based login or verification test completes end-to-end
The agent waits up to **3 minutes** for emails during a test run. See [Email Inbox](/test-features/email-inbox).
***
## 4. Environment & test data
### Environment
Provide a **staging or QA URL** — not production. QA.tech adds it under [**Settings → Applications & Envs**](https://app.qa.tech/current-project/settings/applications). See [Applications and Environments](/core-concepts/applications-and-environments).
If your POC spans multiple apps (customer frontend + admin panel), share each URL separately.
### Test data
The environment must contain **scrubbed, non-sensitive data**:
* No real customer PII (names, emails, addresses, payment details)
* No GDPR-regulated personal data unless anonymized through a documented process
* Repeatable state — accounts and sample records for the journeys you want to demo
| Strategy | Best for |
| ------------------------ | -------------------------------------------- |
| Scrubbed production copy | Realistic data; needs anonymization pipeline |
| Static staging database | Simple POC; data persists |
| Seed scripts with reset | Isolated, repeatable runs |
Seed login accounts, sample records, feature flags, and sandbox modes for third-party integrations (payments, SMS) before kickoff.
***
## 5. WAF & bot protection
If staging sits behind deployment protection or bot detection, configure bypasses before the POC.
| Protection | Guide |
| ---------------------------- | --------------------------------------------------------------------- |
| Cloudflare WAF / Turnstile | [Cloudflare WAF & Turnstile](/configuration/cloudflare-waf-turnstile) |
| Vercel deployment protection | [Vercel Preview](/configuration/vercel-preview-protection) |
| Vercel Firewall | [Vercel Firewall](/configuration/vercel-firewall) |
For HTTP Basic Auth popups on staging, enable **Use for Basic Auth** on a Username + Password [Config](/core-concepts/configs).
***
## Getting help
Contact [QA.tech Support](https://qa.tech/contact) or your QA.tech contact with your staging URL, how it is protected, which auth methods you use, and any error messages from failed tests.
# Custom MCP Integrations
Source: https://docs.qa.tech/integrations/custom-mcp
Connect your own tools and services to the QA.tech AI chat through MCP servers, with per-tool permissions.
Custom MCP Integrations let the QA.tech AI chat use tools from services you already work with, such as a test management system like TestRail. You connect a remote [MCP](https://modelcontextprotocol.io) server, choose which of its tools the chat agent may use, and the agent picks them up alongside its built-in tools. Ask it to sync test cases, look up records, or file items in your other systems, and it calls your server's tools to do it.
This is the reverse direction of the [MCP Server](/integrations/mcp)
integration. That one lets your AI editor call QA.tech. This one lets the
QA.tech chat call your services.
For TestRail and Xray specifically, you do not need a Custom MCP server. Use
the built-in [TestRail](/integrations/testrail) and [Xray](/integrations/xray)
integrations instead. Reach for Custom MCP to connect any other MCP server,
whether you host it yourself or point at a hosted one.
## Set It Up
Go to **Settings → Integrations → Custom MCP Integrations** and click **Add
server**. Give it a name and enter the server URL, which must be a
Streamable HTTP MCP endpoint reachable over HTTPS.
**OAuth** is the default: after saving, you sign in to the service in your
browser and QA.tech handles the rest — no keys to paste. See [OAuth](#oauth)
below for how it works. If your server uses static credentials instead, pick
**Bearer token or API key** and paste the credential your server expects. It
is sent as an `Authorization: Bearer` header on every request and stored
encrypted. Servers without authentication are also supported, and you can
add extra headers such as `X-Api-Key` under **Additional headers** if your
server needs them.
Click **Test connection**. QA.tech connects to the server, lists its tools,
and shows each one with a permission control.
Decide per tool whether the chat agent may use it freely, must ask you
first, or may not use it at all. New tools default to **Needs approval**.
Open the project chat and ask for something the tools can do, for example
"look up the TestRail cases for the checkout suite". Enabled tools are
available to the agent automatically.
## OAuth
Many MCP servers do not issue static API keys and instead use OAuth — the
same flow as a "Sign in with ..." button. QA.tech implements the
[MCP authorization specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization),
so connecting is fully automatic:
* **No configuration.** QA.tech discovers the server's OAuth settings and
registers itself as a client. You never enter endpoints or client IDs.
* **Browser sign-in.** **Add and connect** (or **Connect** on the server card)
sends you to the service's own sign-in page, and back to QA.tech when you
approve. Your password never passes through QA.tech.
* **Automatic refresh.** Tokens are stored encrypted and refreshed in the
background. You only sign in again if the service revokes access — the
server card then shows **OAuth not connected**.
**Reconnect** on the server card repeats the sign-in at any time. Changing the
server URL resets the connection, since the stored tokens only apply to the
original server. The service's authorization server must support automatic
client registration; most MCP servers do, and QA.tech shows a clear error when
one does not.
### Who connects
OAuth servers ask **who connects**:
* **Each member connects their own account** (the default). Tools run with
each person's own permissions in the connected service, so nobody acts
through a teammate's account. When a member opens a chat before connecting,
a banner above the message box asks them to sign in — one click, the
service's sign-in page, and straight back to the chat. If a connection
stops working mid-conversation (for example after a password change), the
failed tool call shows a **Reconnect** button right in the chat.
* **Everyone shares one connection.** One account — typically a service
account — is used for the whole project. Whoever clicks Connect provides
it. Pick this for servers where individual identity doesn't matter.
The server card in settings shows your own connection state for personal
servers ("connected as you") and how many members have connected. Switching
between the two modes signs everyone out of that server.
## Tool Permissions
Every tool runs with the permission you assign. The server-level default
applies to tools that appear later, so a server update never silently gains
unrestricted access.
| Permission | Behavior |
| -------------- | ------------------------------------------------------------------------------------------ |
| Allowed | The agent calls the tool without asking. |
| Needs approval | The chat shows an approval card before each call. Approve or deny it directly in the chat. |
| Denied | The tool is hidden from the agent entirely. |
An approval request expires after 4 minutes. If you deny it or let it expire,
the tool is not executed and the agent is told the call was not permitted.
## Requirements and Limits
| Requirement | Value |
| ----------- | ----------------------------------------------------------------- |
| Transport | Streamable HTTP (stdio-only servers are not supported) |
| URL | HTTPS, reachable from the internet, not a private network address |
| Auth | OAuth (recommended), bearer token, API key, or custom headers |
| Limit | Value |
| ------------------------ | ---------------- |
| Servers per project | 8 |
| Tools per server | 40 |
| Tools across all servers | 60 |
| Headers per server | 16 |
| Header value length | 4,096 characters |
If a server exposes more tools than the limit, the extra tools are skipped.
## Security
* Credentials are stored encrypted and are never shown again after saving. Editing a server keeps the stored values unless you enter new ones.
* OAuth uses the standard authorization-code flow with PKCE. Tokens and the client registration are stored encrypted, and access tokens are refreshed automatically. Signing in happens on the service's own pages, so QA.tech never sees your password.
* Server URLs must resolve to public addresses. Private and internal network ranges are blocked, and redirects are followed at most 5 times with the same checks on every hop.
* Tool output is treated as untrusted input: the agent is instructed not to follow instructions embedded in it.
* An unreachable or misconfigured server never breaks the chat. Its tools are simply unavailable for that conversation. Run **Test connection** to see the error.
## Troubleshooting
| Symptom | Likely cause |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Test connection fails | The URL is not a Streamable HTTP MCP endpoint, or the credential is wrong. Check both and retest. |
| "The server URL must use https" | Only HTTPS endpoints are accepted. Expose the server behind TLS. |
| A tool never shows up in chat | Its permission is **Denied**, or the server is disabled. Check the toggle and per-tool permissions. |
| Tools changed after a server update | Run **Test connection** again to refresh the tool list, then review permissions for new tools. |
| The card shows **OAuth not connected** | The sign-in was never completed, or the service revoked access. Click **Connect** to sign in again. |
| "does not support automatic client registration" | The service's authorization server lacks dynamic client registration. Use a bearer token or API key instead, or ask the vendor to enable it. |
| Chat says tools need authentication | The server uses personal connections and you haven't signed in yet. Use the banner above the message box, or Connect on the server card in settings. |
For anything else, [contact support](https://qa.tech/contact).
# Jira
Source: https://docs.qa.tech/integrations/jira
Let the QA.tech agent read Jira issues for PR reviews and test planning
Connect **Jira Cloud** to QA.tech so agents can **read** issues from your configured project — titles, descriptions, acceptance criteria, status, and more. That context powers smarter testing during [PR reviews](/configuration/github-app) and when you plan tests in the [AI Chat Assistant](/core-concepts/ai-chat-assistant).
QA.tech does **not** sync Jira in the background. Issues are fetched on demand when an agent or chat request needs them. You can also create Jira tickets from chat, or manually export a detected test issue (legacy workflow).
**Requirements:** Jira **Cloud** only (Atlassian OAuth). Self-hosted Jira
Server or Data Center is not supported. Connect at the organization level,
then pick a Jira project and issue type in project settings.
## What the Agent Reads from Jira
Reads are scoped to the **Jira project you configure** in project settings. QA.tech uses your OAuth connection to call the Jira Cloud API on your behalf.
### Issue fields
For each issue the agent fetches or searches, QA.tech reads: **Key**, **Title** (summary), **Description**, **Status**, **Assignee**, **Priority**, **Issue type**, **Labels**, and **URL**.
### Search behavior
The agent uses **plain-text keywords** — not raw JQL. You ask in natural language; QA.tech builds the query.
| You ask for… | What QA.tech does |
| ---------------- | ---------------------------------------------------------------- |
| A specific key | Fetches that issue directly (e.g. `PROJ-108`) |
| Keyword search | Searches issue text for your keywords in the configured project |
| Recent issues | Returns up to **25** most recently updated issues in the project |
| Issues of a type | Filters by issue type name: `Bug`, `Story`, `Epic`, `Task`, etc. |
Keyword search matches text in issues — it does **not** filter by status (e.g. "open" vs "done"). If you need a specific ticket, ask by key (`PROJ-108`) or describe the topic.
QA.tech does not read boards, sprints, release versions, epics as hierarchy,
custom fields, attachments, comments, or user directories. It cannot
automatically list "all stories in release 2.4" from Jira metadata.
## How Agents Use Jira Data
### PR and merge request reviews (automatic)
When [GitHub PR review](/configuration/github-app) or [GitLab MR review](/configuration/gitlab) runs, the review agent looks for Jira keys in the PR/MR title, branch name, or commits (e.g. `PROJ-456`).
If a key is found and Jira is connected, the agent **fetches that ticket** and uses the description and acceptance criteria to:
* Understand what the change is meant to do
* Select relevant existing tests
* Create focused tests for gaps
* Scope the review to user-facing behavior described in the ticket
The agent does not create or update Jira issues during review.
> **Example:** A PR titled `PROJ-789 Add coupon field to checkout` triggers a fetch of `PROJ-789`. The agent reads the story description, runs checkout tests, and verifies the coupon behavior matches the ticket.
### Chat-assisted test planning (on demand)
In the [AI Chat Assistant](/core-concepts/ai-chat-assistant), you can ask the agent to pull Jira context before creating or running tests:
> "Pull up PROJ-108 and generate tests based on its acceptance criteria"
> "Find Jira stories about checkout and suggest tests based on their descriptions"
> "List recent bugs in Jira — I want to add regression tests for the top two"
> "Fetch PROJ-201, PROJ-202, and PROJ-203 — then start a release check for those changes"
The assistant returns issue details (including status) you can use to author tests, run existing tests, or describe intended changes for a release check in chat. You provide the ticket keys or search terms — QA.tech does not auto-discover release contents from Jira versions or sprints.
### Creating issues in Jira
The chat assistant can also **create** standalone Jira tickets (summary, description, `qatech` label) in your configured project. It does not update existing issues.
## Setup
Go to **Settings → Integrations** in your project settings. Click **Manage
Connections**, then connect Jira. Complete the Atlassian OAuth flow.
Return to **Settings → Integrations** and open Jira. Select the **Jira
project** and **issue type** for agent reads and creates, then click **Save
Jira Integration**.
Use **Create Test Issue** after saving to confirm the connection works.
## Jira and Linear Together
If both [Linear](/integrations/linear) and Jira are connected, mention which tracker you want:
> "Search **Jira** for checkout bugs"
If you do not specify, the assistant defaults to **Linear** when both are configured.
## Exporting Detected Issues
QA.tech can also push a [detected test issue](/core-concepts/issues) to Jira manually — one ticket per issue, with test steps and evidence. This is a separate workflow from agent reads; most teams use Jira → QA.tech (read for testing) rather than QA.tech → Jira (export failures).
From the **Issues** page, click **Create ticket**, or from a test run choose **Send to Jira**. Exported tickets include a `qatech` label and a link back to QA.tech.
## Limitations
| Supported | Not supported |
| --------------------------------------- | --------------------------------------------------- |
| Read issues in configured project | Read across all projects in your site |
| Fetch by issue key | Pass raw JQL in chat |
| Keyword and issue-type search | Filter by status, sprint, board, or release version |
| PR/MR review fetches linked ticket keys | Auto-list stories in a Jira release or sprint |
| Chat fetch/search on demand | Background sync or webhooks |
| Create issues (chat + manual export) | Update, comment, or transition issues |
| Jira Cloud (OAuth) | Jira Server / Data Center |
## Related
* [AI Chat Assistant](/core-concepts/ai-chat-assistant) — search and create tracker issues from chat
* [GitHub App for PR Reviews](/configuration/github-app) — automatic PR testing with linked ticket lookup
* [GitLab MR Reviews](/configuration/gitlab) — same pattern for GitLab
* [Issues](/core-concepts/issues) — detected test issues and manual export
* [Linear](/integrations/linear) — alternative issue tracker integration
# Linear
Source: https://docs.qa.tech/integrations/linear
Export issues to Linear and manage them from the AI Chat Assistant
The Linear integration allows you to manually export QA.tech issues to your Linear workspace. Issues are **not automatically synced** - you choose which issues to send to Linear.
**Prerequisites:** QA.tech automatically detects issues during test runs. You
can set up the integration without any issues, but you'll need at least one
detected issue before you can export. See [Issues](/core-concepts/issues) to
learn what types of issues are detected.
## Setup
Go to **Settings → Integrations** in your project settings. Click **"Manage
Connections"** at the top, then add the Linear integration. Follow the OAuth
flow to grant QA.tech access to your Linear workspace.
Return to **Settings → Integrations** and click on the Linear integration.
Select the **Linear Team** where you want exported issues to appear, then
click **"Save Linear Integration"**.
## Exporting Issues
Once configured, you can export any QA.tech issue to Linear:
Click **Issues** in the sidebar to see detected issues.
Click on an issue to view its details.
In the **Link** section, click **"Create issue"**. The issue will be created
in your configured Linear team.
## What Gets Exported
When you export an issue, Linear receives:
* **Title:** The issue title from QA.tech
* **Description:** Includes:
* Issue type
* First seen date
* Issue description
* Help/hint information (if available)
* Link back to QA.tech
* Console error details (for console errors: error level, file location, line numbers, messages)
Exported issues include a direct link back to QA.tech, so your team can easily
access test results and occurrence details.
## Using Linear in Chat
Once your Linear connection is set up, the [AI Chat Assistant](/core-concepts/ai-chat-assistant) can search your Linear issues and create new ones directly from the chat interface.
You can ask the assistant to:
* **Search issues** by keyword or topic
* **Fetch a specific issue** by its key
* **List recent issues** from your connected team
* **Create new issues** with a title and description in your configured Linear team
> "Search Linear for issues related to checkout"
> "Show me DEV-42"
> "What are the most recent issues in Linear?"
> "Create a Linear issue titled 'Fix mobile nav overlap' with a description of the layout bug on small screens"
If you have both Linear and Jira connected, mention which tracker you want
(e.g. "search **Linear**"). If you do not specify, the assistant defaults to
Linear when both are configured.
# MCP Server
Source: https://docs.qa.tech/integrations/mcp
Connect Claude Code, Cursor, Codex, or Continue to QA.tech so your AI assistant can list test cases, start runs, and read results.
The QA.tech MCP server exposes your test cases, runs, and applications to any AI client that speaks the [Model Context Protocol](https://modelcontextprotocol.io). Once connected, your assistant can answer questions like *"what runs failed today?"* or *"rerun the failed cases from run UkxK"* without you leaving the chat.
## Accessing the MCP
The MCP is available at:
```
https://api.qa.tech/v1/mcp
```
Authentication is handled via OAuth.
* On first connect you sign in to QA.tech and pick an organization.
* You can only access data you have permission to view in that organization.
API keys still work for CI and clients that don't speak OAuth yet. Open the dashboard at **Organization Settings → MCP Server** for copy-paste client snippets.
## Pick Your Client
Most modern clients have a one-click or one-line install. Clients that support MCP OAuth (Claude Code, Cursor, Codex) only need the URL above. API keys still work for CI and clients that don't do OAuth yet.
Run this in any project where you use Claude Code. Requires the [Claude Code CLI](https://claude.ai/code).
```bash theme={null}
claude mcp add --transport http qatech \
'https://api.qa.tech/v1/mcp'
```
Then run `/mcp` in Claude Code (or `claude mcp login qatech`) and complete
the browser sign-in. Claude Code reloads MCP servers automatically.
Prefer a static key instead? Add
`--header 'Authorization: Bearer '` from
**Organization Settings → API Keys**.
Go to **Settings → Cursor Settings → Tools & MCPs → New MCP server**. Cursor
opens `~/.cursor/mcp.json`. Paste this in — Cursor will open a browser
window for you to sign in to QA.tech and approve access:
```json theme={null}
{
"mcpServers": {
"qatech": {
"type": "http",
"url": "https://api.qa.tech/v1/mcp"
}
}
}
```
Prefer a static key instead? Add an `Authorization` header with
`Bearer ` from **Organization Settings → API Keys**.
Run this command to add the MCP server to Codex. Requires the Codex CLI.
Codex starts the OAuth browser flow when the server supports it; you can
also run `codex mcp login qatech`.
```bash theme={null}
codex mcp add qatech \
--url 'https://api.qa.tech/v1/mcp'
```
Prefer a static key instead? Add
`--header 'Authorization: Bearer '`.
First time using an MCP server in Codex? Add `experimental_use_rmcp_client = true` under `[features]` in `~/.codex/config.toml`.
Continue uses a static API key. Add this snippet to `~/.continue/config.yaml`, then reload the Continue extension.
```yaml theme={null}
mcpServers:
- name: qatech
type: streamable-http
url: https://api.qa.tech/v1/mcp
requestOptions:
headers:
Authorization: Bearer
```
## Or Use the QA.tech CLI
The QA.tech CLI can write the config for any supported client in one command. Useful if you switch machines often or want the same setup scripted.
Don't have the QA.tech CLI? Install it with `npm install -g @qadottech/cli`.
For Cursor and Claude Desktop, the default is a URL-only OAuth entry (no API key):
```bash theme={null}
qatech mcp configure --client cursor
qatech mcp configure --client claude-desktop
```
Clients that still need a Bearer token (Continue, Goose) require an API key first:
```bash theme={null}
qatech configure -k
qatech mcp configure --client continue
```
Pass `--api-key` to force a static Bearer entry for Cursor/Claude Desktop. Add `--print` to dump the snippet without writing it. See `qatech mcp configure --help` for all flags.
## What Your Assistant Can Do
These tools are exposed. Tools requiring `write` scope are hidden from read-only API keys. OAuth grants include both `read` and `write`.
| Tool | Scope | What it does |
| ------------------- | ----- | ------------------------------------------------------------------------------------------ |
| `list_applications` | read | Lists applications under test in the project the API key is bound to. |
| `list_test_cases` | read | Lists test cases, optionally filtered by application, labels, or enabled state. |
| `get_run` | read | Fetches a run by short ID. Include nested results with `testCases: "all"` or `"failed"`. |
| `list_issues` | read | Lists issues QA.tech found in the project, newest first, with `severity`/`status` filters. |
| `get_issue` | read | Fetches one issue by short ID, including the run test cases where it was detected. |
| `create_test_case` | write | Creates a new test case in draft. Burn-in runs start automatically. |
| `rerun_run` | write | Reruns a previous run. Optional `failedOnly`, or a specific `projectTestCaseIds` subset. |
### Example prompts
> "Which of my test cases failed in the last run?"
> "Rerun run UkxK, but only the failed cases."
> "List the critical issues QA.tech found this week, then help me fix the first one."
> "Create a test case titled 'Checkout with expired card' for the frontend app."
> "List all enabled test cases tagged 'critical'."
## Authentication
Two options:
### OAuth (recommended for interactive clients)
Claude Code, Cursor, Codex, and other MCP clients that support OAuth discover
QA.tech's authorization server automatically. On first connect you sign in with
your QA.tech account, pick an organization, and approve access. Tokens refresh
in the background. OAuth grants include both `read` and `write` tools for that
organization.
### API key
Same Bearer tokens as the [REST API](/api-reference/introduction). Useful for CI
and clients that don't speak OAuth yet.
* **Where to find your key:** **Organization Settings → API Keys** in the dashboard.
* **Scopes:** A `read` key gives access to the four read tools. A `write` key adds `create_test_case` and `rerun_run`.
* **Project binding:** Keys may be org-scoped or project-scoped. Org-scoped keys need a `projectShortId` on tool calls.
Treat API keys as confidential credentials. Anyone with the token can read
your test data and, with a write key, create and rerun tests. Rotate keys from
the dashboard if one leaks.
## Endpoint Details
| Field | Value |
| ----------------- | ---------------------------------------------------------- |
| URL | `https://api.qa.tech/v1/mcp` |
| Transport | Streamable HTTP (JSON-RPC 2.0), stateless |
| Methods supported | `initialize`, `tools/list`, `tools/call` |
| Auth | OAuth (MCP discovery) or `Authorization: Bearer ` |
| Protocol version | `2025-06-18` |
| Server name | `qatech` |
Batch JSON-RPC requests and `GET` upgrades are not supported. Each call is a single `POST` with a JSON body.
### Smoke test with curl
```bash theme={null}
curl -sS -X POST 'https://api.qa.tech/v1/mcp' \
-H 'Authorization: Bearer ' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```
A successful response lists the tools your key has access to.
## Troubleshooting
| Symptom | Likely cause |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| Client lists no QA.tech tools | Restart the client after editing config. Verify the JSON or YAML parses. |
| Browser never opens for OAuth | Confirm the client supports MCP OAuth and the config has no `Authorization` header (URL only). Retry from the client's MCP login / `/mcp` UI. |
| `401` or `403` from the server | OAuth consent not completed, wrong org, or API key missing/revoked. Re-auth or re-copy the key. |
| Some tools missing | API key lacks `write` scope. Generate a write key, or use OAuth (includes write). |
| `405 Method Not Allowed` | The client sent a `GET`. QA.tech MCP is `POST`-only and stateless. |
| `qatech mcp configure` says no API key | Continue/Goose need a key: run `qatech configure -k ` or pass `--api-key`. Cursor/Claude Desktop default to OAuth and do not need one. |
For anything else, [contact support](https://qa.tech/contact).
# Microsoft Teams
Source: https://docs.qa.tech/integrations/microsoft-teams
Receive QA.tech notifications in Microsoft Teams channels
The Microsoft Teams integration sends automated test run notifications directly to your Teams channels using incoming webhooks. This is a notification-only integration - you'll receive alerts when test runs complete. This integration does not support interactive features. Per-run overrides can change `notifyOn` or disable Teams for a single run; they always use the project webhook URL.
## Setup
In Microsoft Teams, open the option menu on the channel where you want to receive notifications, then select **Workflows** from the menu.
Select the **"Send webhook alerts to a channel"** template. This creates a workflow that posts messages to your channel when it receives a webhook request.
Once the workflow is created, you'll see a dialog with a webhook URL. Copy this URL - you'll need it for the next step.
For detailed instructions on creating incoming webhooks in Microsoft Teams, see [Microsoft's official documentation](https://support.microsoft.com/en-us/office/create-incoming-webhooks-with-workflows-for-microsoft-teams-8ae491c7-0394-4861-ba59-055e33f75498).
If the **Workflows** option is not available in your channel menu, you can also create the webhook workflow from the [Workflows app in Microsoft Teams](https://support.microsoft.com/en-us/office/browse-and-add-workflows-in-microsoft-teams-4998095c-8b72-4b0e-984c-f2ad39e6ba9a). Search for the **"Send webhook alerts to a channel"** template and follow the setup steps.
Go to [**Settings → Integrations**](https://app.qa.tech/current-project/settings/integrations) in your QA.tech project. Find the **Microsoft Teams** section and paste the webhook URL you copied from Teams.
Click **Save** to enable the integration.
After saving, you can use the **Send Test Notification** button to verify your webhook is working correctly.
## What You Get
Each notification includes:
* **Run status** - Pass or fail indication
* **Test plan name** - If the run was part of a test plan
* **Timing** - When the run started and total duration
* **Error tests** - Breakdown of any tests that encountered errors
* **Failed tests** - Breakdown of any tests that failed
* **Skipped tests** - Breakdown of any tests that were skipped
* **Results link** - Direct link to the full test report in QA.tech
You'll receive a notification for every completed run, whether triggered manually, through CI/CD, or via schedule.
## Troubleshooting
### Notifications not appearing
* Verify the webhook URL is correct and hasn't expired
* Check that the workflow in Microsoft Teams is still active
* Ensure the channel still exists and the workflow has permission to post
### Need to update the webhook URL
If you need to regenerate or change your webhook URL:
1. Open the Workflows app in Microsoft Teams
2. Find your webhook workflow and select **Edit**
3. Expand the trigger **"When a Teams webhook request is received"** to copy the URL
4. Update the URL in QA.tech under **Settings → Integrations**
# Slack
Source: https://docs.qa.tech/integrations/slack
AI-powered testing assistance directly in your Slack workspace
# QA.tech Slack Bot
The QA.tech Slack Bot brings AI-powered testing assistance directly into your Slack workspace, allowing your team to create test cases, report bugs, and get QA guidance without leaving Slack.
## What It Does
**Smart AI Assistant**: Mention `@QA.tech` in any channel to get intelligent QA assistance powered by advanced AI.
**Test Case Generation**: Describe a feature or bug, and the bot will suggest comprehensive test cases that you can review and add to your QA.tech project.
**Bug Reporting & Investigation**: Report bugs in natural language and get suggested test cases to reproduce the issue and prevent regressions.
**Project Context**: The bot automatically understands which QA.tech project you're working on based on your workspace settings.
## Getting Started
Navigate to [**Settings →
Integrations**](https://app.qa.tech/current-project/settings/integrations?focus=slack).
Find the **Slack Bot** section and click **Install** to connect your Slack
workspace.
Add `@QA.tech` to relevant channels where your team discusses bugs,
features, and testing.
Mention the bot with `@QA.tech` to get started with AI-powered testing
assistance.
**Beta Release**: The QA.tech Slack Bot is currently in beta. You may
encounter issues or limitations as we continue to improve the experience.
Please provide feedback using 👍/👎 reactions on bot responses, or [contact
us](https://qa.tech/contact) with any questions or suggestions.
## Slack Notifications
The QA.tech Slack Bot also supports Slack notifications for test runs. Configure Slack notifications to receive run completion alerts and use per-run channel overrides to send notifications to specific channels. See [Notifications](/core-concepts/notifications) for setup instructions and API details.
# Status Badges
Source: https://docs.qa.tech/integrations/status-badges
Visual indicators for your project test status
# Status Badges
Status badges are visual indicators that show the current state of your QA tests. They automatically update whenever new tests are run, providing real-time feedback on your project's quality.
## What are Status Badges?
Status badges display the current status of your QA tests in a simple, visual format. They can indicate whether tests are passing, failing, or were skipped based on your most recent test run.
## Test Plan Integration
Status badges work seamlessly with test plans. You can:
* Create separate badges for different test plans
* Track the health of specific workflows or features
* Monitor critical paths independently from other tests
* Set up dedicated badges for development, staging, and production environments
## Getting Started
Navigate to [**Settings →
Integrations**](https://app.qa.tech/current-project/settings/integrations?focus=status-badge).
Find the **Status Badge** section and toggle it to enable.
Select which test plan to track with your badge and customize the appearance
options as needed.
Copy the generated Markdown, HTML, or URL code to embed the badge in your
README, documentation, or website.
# TestRail
Source: https://docs.qa.tech/integrations/testrail
Pull your TestRail test cases into the QA.tech AI chat
The TestRail integration lets the [AI Chat Assistant](/core-concepts/ai-chat-assistant) read your existing TestRail test cases, so you can explore them and turn them into QA.tech tests without re-authoring anything by hand.
**Authentication:** TestRail uses an email + API key (HTTP Basic auth). Your
API key is stored encrypted and is never shown again after saving.
## Before you start
Enable the API and generate a key in TestRail:
In TestRail, go to **Administration → Site Settings → API** and turn on
**Enable API**. This requires TestRail administrator access.
Click your avatar → **My Settings → API Keys → Add Key**, then copy the
generated key — TestRail shows it only once. See the [TestRail API
docs](https://support.testrail.com/hc/en-us/articles/7077039051284-Accessing-the-TestRail-API)
for details.
## Setup
Go to **Settings → Integrations** in your project and select **TestRail**.
Fill in:
* **Instance URL** — your TestRail address, e.g. `https://yourco.testrail.io`
* **Account email** — the email the API key belongs to
* **API key** — the key you generated above
Then click **Connect TestRail**.
Click **Test connection** to confirm QA.tech can reach your instance — it
reports how many projects it can see.
Your TestRail instance must be reachable over **https** on a public address.
Internal-only / private-network instances are not supported.
## Using TestRail in chat
Once connected, ask the [AI Chat Assistant](/core-concepts/ai-chat-assistant) to
work with your TestRail cases. It can:
* **List your TestRail projects** to find the right one
* **List test cases** in a project, suite, or section (paginated for large projects)
* **Fetch a case in full**, including its steps and preconditions
* **Turn a TestRail case into a QA.tech test**
> "List our TestRail projects"
> "Show the test cases in the Checkout suite"
> "Open TestRail case C1234 and create an equivalent QA.tech test"
For multi-suite projects, tell the assistant which suite you mean (or ask it
to list the suites first) so it can scope the cases correctly.
## Removing the integration
To disconnect, open **Settings → Integrations → TestRail** and click **Remove**.
This deletes the stored credentials, including your API key. Test cases you have
already imported into QA.tech are not affected.
# Trello
Source: https://docs.qa.tech/integrations/trello
Export QA.tech-detected issues to Trello
The Trello integration allows you to manually export QA.tech issues to your Trello board. Issues are **not automatically synced** - you choose which issues to send to Trello.
**Prerequisites:** QA.tech automatically detects issues during test runs. You
can set up the integration without any issues, but you'll need at least one
detected issue before you can export. See [Issues](/core-concepts/issues) to
learn what types of issues are detected.
## Setup
Go to **Settings → Integrations** in your project settings. Click **"Manage
Connections"** at the top, then add the Trello integration. Follow the OAuth
flow to grant QA.tech access to your Trello workspace.
Return to **Settings → Integrations** and click on the Trello integration.
Select the **Trello board** and **list** where you want exported cards to
appear, then click **"Save Trello Settings"**.
## Exporting Issues
Once configured, you can export any QA.tech issue to Trello:
Click **Issues** in the sidebar to see detected issues.
Click on an issue to view its details.
In the **Link** section, click **"Create task"**. A card will be created in
your configured Trello list.
## What Gets Exported
When you export an issue, Trello receives:
* **Card Title:** The issue title from QA.tech
* **Description:** Full issue details with a link back to QA.tech
Exported cards include a direct link back to QA.tech, so your team can easily
access test results and occurrence details.
# Testing API Calls
Source: https://docs.qa.tech/test-features/api-calls
Configure API endpoints (URL, method, headers, body) to make HTTP requests during test execution
The API Calls feature allows your AI agent to make HTTP requests to external APIs during test execution. This enables data fetching, authentication workflows, integration testing, and API validation.
## What Can You Do With API Calls?
* **Fetch test data or credentials** from your backend before testing UI workflows
* **Authenticate via API** to get tokens or sessions for protected pages
* **Validate data** by checking API responses match what you expect
## Setup: Creating an API Call Config
API Calls are configured as **Configs** in your project settings. The URL and request details are stored in the config, so you can easily reuse them across multiple tests.
Navigate to **Settings → Configs** in your project dashboard, then click
**Add config** and select **API Call Configuration**.
Complete the form fields: | Field | Description | |-------|-------------| |
**Config Name** | A descriptive name (e.g., "Get Test User") | | **URL** | The
complete API endpoint URL (e.g., `https://api.example.com/v1/users`) | |
**Method** | Select HTTP method (GET, POST, PUT, DELETE, PATCH). Defaults to
GET. | | **Headers** | (Optional) JSON object with HTTP headers (paste into
the textarea) | | **Body** | (Optional) JSON string for the request body (for
POST/PUT/PATCH) |
Click **Save** to create the configuration. You can then assign this config
to any test case in the test's **Settings → Configs** panel.
The **Headers** and **Body** fields use textareas where you paste JSON. The UI
will validate your JSON format before saving.
**Security: Use Test Credentials Only**
API call configurations (including headers, tokens, and request bodies) are:
* Stored in your project settings in plain text
* Passed directly to AI language models during test execution
* Visible in network logs and screenshots
**Always use test credentials.** Never use production API keys or credentials with sensitive access.
## Configuration Examples
### Example 1: GET Request with Authentication
This example shows how to fetch user data with an API key.
**Fill the form fields:**
| Field | Enter This |
| --------------- | -------------------------------------- |
| **Config Name** | `Get User Data` |
| **URL** | `https://api.example.com/v1/users/123` |
| **Method** | **GET** |
**In the Headers textarea:**
```json theme={null}
{
"Authorization": "Bearer your-test-token-here",
"Content-Type": "application/json"
}
```
### Example 2: POST Request with Body
This example shows how to send data to your API.
**Fill the form fields:**
| Field | Enter This |
| --------------- | ---------------------------------- |
| **Config Name** | `Create Test User` |
| **URL** | `https://api.example.com/v1/users` |
| **Method** | **POST** |
**In the Headers textarea:**
```json theme={null}
{
"Authorization": "Bearer your-test-token-here",
"Content-Type": "application/json"
}
```
**In the Body textarea:**
```json theme={null}
{
"name": "Test User",
"email": "testuser@example.com",
"role": "standard"
}
```
## Viewing API Results
When a test runs, you can inspect the full details of every API call made by the agent.
1. Open the **Run Details** page for your test.
2. In the **Action Log** (the trace view on the left), click on the step where the API call occurred.
3. The detail panel will show an **API Requests** view (indicated by a network icon).
### Available Details
The API Request view provides a comprehensive breakdown:
* **Summary**: Method (color-coded), Status Code, and URL path.
* **Copy as cURL**: A button to copy the exact request as a cURL command for debugging.
* **Request Details**:
* **Query Parameters**: Parsed list of URL parameters.
* **Headers**: Full list of request headers.
* **Body**: The JSON or text body sent.
* **Response Details**:
* **Headers**: Response headers received from the server.
* **Body**: The full JSON or text response.
## Overriding Config Values
**Why override?** You might want to use the same Auth headers but call a different endpoint or use a different ID in the URL.
**How to override:**
In your test instructions, simply tell the agent what to change. The agent will use the base config (Headers, Method) but swap out the URL or Body as requested.
**Example:**
*Config:* `Get User` (URL: `.../users/1`)
*Test Step:* "Call the API with URL `.../users/555`"
The agent will keep the authentication headers from the config but fetch user 555.
## Response Format for the Agent
The agent receives the API response in a structured format it can understand and use in subsequent steps:
```json theme={null}
{
"statusCode": 200,
"statusText": "OK",
"data": {
"userId": 123,
"email": "john@example.com"
}
}
```
This allows the agent to:
1. **Extract data**: "Get the email from the API response and type it into the login form."
2. **Verify success**: "Check that the API returned status 200."
## Limitations
* **No SSH Tunnel**: API calls originate directly from our servers, not through your SSH tunnel.
* **No Variables**: Headers and bodies must be static JSON (unless overridden by natural language instructions in test steps).
* **Public Access**: APIs must be publicly accessible or allow QA.tech IPs.
# Device Presets
Source: https://docs.qa.tech/test-features/device-presets
Configure browser sessions with predefined device settings
Device presets allow you to configure browser sessions with predefined device settings, making it easy to test your application across different device configurations. When using device presets, you can simulate specific devices with their viewport sizes, user agents, and other device-specific settings.
## Overview
Device presets let you configure browser sessions with predefined device settings, making it easy to test your application across different device configurations. Use device presets to:
* Test responsive design across mobile, tablet, and desktop layouts
* Test localization with different locales and timezones (e.g., EU vs US users)
* Test accessibility preferences like dark mode and reduced motion
* Test browser-specific behavior (e.g., Safari mobile quirks)
* Add custom authentication headers for staging environments
## When to Create Separate Presets
Create separate device presets when you need to:
| Use Case | Example | What to Configure |
| ------------------------------ | ------------------------- | ----------------------- |
| Test responsive design | Mobile vs desktop layouts | Device type, resolution |
| Test localization | EU vs US users | Locale, timezone |
| Test accessibility preferences | Dark mode, reduced motion | Accessibility settings |
| Test browser-specific behavior | Safari mobile quirks | User agent, device type |
| Add authentication headers | Staging with basic auth | Custom headers |
See [Applications and Environments](/core-concepts/applications-and-environments) for how device presets fit into the overall test configuration hierarchy.
## Mobile Device Presets
For **native mobile apps** (iOS and Android), device presets configure the cloud simulator or emulator — not a browser viewport. Mobile testing must be enabled for your organization.
Create mobile presets under [**Settings → Device Presets**](https://app.qa.tech/current-project/settings/device-presets) in the **iOS Device Presets** or **Android Device Presets** section. Each preset controls:
| Setting | Description |
| ------------ | --------------------------------------------------------------- |
| Platform | iOS or Android |
| Device model | Default simulator/emulator or a specific device |
| OS version | Latest, exact, minimum, or range |
| Orientation | Portrait or landscape |
| Location | Default, a region, or custom GPS coordinates |
| Network logs | Capture HTTP requests during tests (viewable in the test trace) |
Mobile presets use the same [priority order](#priority-order) as web presets. Override at run time via the [AI Chat Assistant](/core-concepts/ai-chat-assistant) or API using `devicePresetShortId`.
Network log capture routes traffic through a proxy. If your app fails to load
data during tests, turn off **Capture Network Logs** on the device preset.
See [Mobile App Testing](/test-features/mobile-app-testing) for build upload, IP whitelisting, and mobile-specific limitations.
## Available Settings
Device presets include the following settings:
* **Device Type**: Choose between Desktop, Tablet, or Mobile configurations
* **Resolution**: Each device type comes with predefined viewport sizes
* **Browse From**: Route the browser's traffic through a proxy so it exits from a chosen location's IP
* **Locale**: Set the browser's language and region settings (e.g., en-US, sv-SE)
* **Timezone**: Configure the device's timezone (e.g., Europe/Stockholm)
* **Custom Headers**: Add custom HTTP headers for special testing requirements
## Managing Device Presets
### Creating a Preset
Go to [**Settings → Device
Presets**](https://app.qa.tech/current-project/settings/device-presets)
Click the "Create Device Preset" button
Fill in the required settings: - Name your preset - Select a device type
(Desktop/Tablet/Mobile) - Configure locale and timezone - Optionally
override the default resolution - Optionally override the default user agent
If needed, add custom HTTP headers: - Click "Custom Headers" - Select a
header name or type your own - Enter the header value - Click the plus icon
to add more
Toggle "Set as default preset" if you want this to be the project default
Click "Create" to save your preset
### Resolution Settings
Each device type comes with a default resolution:
* **Desktop**: 1280×800 (recommended)
* **Tablet**: 768×1024
* **Mobile**: 375×667
You can override these defaults using the resolution toggle in the preset configuration.
> **Note**: Using resolutions larger than 1280×800 may result in slower test execution. Our testing model is optimized for the default desktop resolution (1280×800) to provide the best balance of coverage and performance.
### User Agent
Each device type comes with an appropriate default user agent. You can override this using the user agent toggle if needed for specific testing scenarios.
### Browse From, Locale, and Timezone
**Browse from**, **Locale**, and **Timezone** each use the same **Override** toggle as Resolution and User Agent. When a toggle is off, the field follows an automatic default and the description shows what that value is. When you turn it on, you set the value manually.
**Browse from** controls where the browser's traffic appears to originate. With the toggle off, the browser uses the **default location** and traffic exits from QA.tech's own IP. Turn the toggle on to pick a location, and the browser routes through a proxy there so requests exit from a local IP. Use it to test geo-specific behavior such as localized content, regional pricing, or location-based redirects. Locations are grouped into **Countries** and **United States** (individual states); the available set is curated by QA.tech and may change over time.
**Locale** and **Timezone** follow your Browse from choice automatically:
| Locale / Timezone toggle | Browse from | Value used |
| ------------------------ | ----------------- | ----------------------------------------------- |
| Off | Default location | The device preset's default locale and timezone |
| Off | A picked location | The locale and timezone matching that location |
| On | Any | Your manually entered value |
When a Locale or Timezone toggle is off, the field shows whether it is using the preset default or matching the Browse from location, so the effective value is always visible.
Picking a Browse from location updates Locale and Timezone to match that
location **only while their own Override toggles are off**. If you turn on the
Locale or Timezone override, your manual value is kept and is never changed by
a Browse from selection.
If your project uses an [SSH tunnel proxy](/configuration/ssh-tunnel), turning
on Browse from and picking a location overrides the tunnel for that preset, so
traffic will not go through SSH. Leave Browse from off (default location) to
keep using the SSH tunnel.
### Custom Headers
You can add custom HTTP headers to be sent with **all requests** from this device (navigation, resources, API calls, redirects). This is useful for:
* Adding authentication headers (e.g., `Authorization: Bearer token`)
* Bypassing bot protection (e.g., `X-Vercel-Code: your-code`)
* Adding API keys (e.g., `X-API-Key: your-key`)
* Custom request identification
**How to add headers:**
1. Expand the "Custom Headers" accordion section
2. Select a header name from the autocomplete dropdown (47 common headers available) or type your own
3. Enter the header value
4. Click the plus icon (+) to add the header
5. Repeat to add multiple headers
6. Click the trash icon to remove a header
**Header validation:**
* Header names must follow RFC 7230: letters, numbers, hyphens, underscores, and periods only (e.g., `X-API-Key`, `Authorization`)
* Header values can contain printable ASCII characters
* Both name and value are validated before saving
**CORS Considerations:** Custom headers may cause CORS errors if the target
server doesn't include them in `Access-Control-Allow-Headers`. This could
prevent requests that would otherwise succeed.
### Default Presets
Each project can have one default device preset. When set, this preset will be used for all tests unless overridden by higher-priority settings. See [Priority Order](#priority-order) for the complete hierarchy of how device presets are selected.
## Using Device Presets in AI Chat
You can discover and work with device presets through the [AI Chat Assistant](/core-concepts/ai-chat-assistant) using natural language. The assistant can show you available presets and help you create or run tests with specific device configurations.
**Example: Creating tests for mobile**
Say you want to create tests that run on mobile devices. Simply ask:
> "Create a test for the checkout flow on mobile"
The assistant will:
1. Show you available mobile device presets
2. Ask which specific preset you'd like to use
3. Create the test configured with the selected device preset
**Example: Running tests with a different device**
You can also override device presets when running existing tests:
> "Run the login test on an iPhone"
The assistant will show available mobile presets and run the test with your selected device configuration, overriding the test's default preset for that run.
**More example queries:**
* "What device presets do I have?"
* "Show me all mobile presets"
* "Can I test on tablet?"
* "Create checkout tests for mobile and desktop"
This makes it easy to explore your device options and work with device-specific testing without navigating through settings pages. Learn more about chat capabilities in the [AI Chat Assistant documentation](/core-concepts/ai-chat-assistant).
## Priority Order
Device preset selection follows this priority order:
1. AI Chat runtime override (when specified)
2. Test case setting (configured when editing a test case)
3. Test plan setting (see [Test Plans](/core-concepts/test-plans))
4. Project default preset
5. QA Tech default preset (fallback)
## Integration with Test Plans
Device presets can be configured at the test plan level, allowing you to run the same test cases with different device configurations. This is particularly useful for:
* Testing responsive design across multiple devices
* Verifying functionality on specific device types
* Running the same test plan with different locale/timezone settings
When a test plan has a device preset configured, it takes precedence over project defaults but can still be overridden by individual test case settings. Learn more about test execution and configuration in our [Test Plans documentation](/core-concepts/test-plans).
### Setting Device Presets in Test Plans
Navigate to your test plan and click "Settings"
Choose a device preset from the dropdown menu
Click "Save" to apply the device preset to your test plan
All test runs initiated from this test plan will now use the selected device preset unless overridden by test case settings.
### Multi-Device Testing Strategy
To test the same functionality across different devices, you have two options:
**Option 1: Separate Test Plans**
1. Create device presets for each target device (e.g., "iPhone 14", "Galaxy Tab", "Desktop Chrome")
2. Create separate test plans that use different device presets
3. Run the appropriate test plan in your pipeline or schedule
**Option 2: API Overrides**
Override device presets per-run via API using the `devicePresetShortId` parameter in the `applications` array. This allows you to test different device configurations without creating multiple test plans. See [Start Run API](/api-reference/runs/start-test-run) for details.
The [GitHub App](/configuration/github-app) can automatically select device presets based on PR content - for example, testing on mobile when a PR mentions "responsive design".
## Accessibility Emulation
Test how your app responds to user accessibility preferences:
| Setting | Values | Purpose |
| ------------------ | -------------------------------- | ----------------------------- |
| **Color Scheme** | `light`, `dark`, `no-preference` | Test dark mode |
| **Forced Colors** | `active`, `none` | Test high contrast mode |
| **Reduced Motion** | `reduce`, `no-preference` | Test with animations disabled |
Configure in Advanced Settings when creating/editing device presets.
These settings **emulate user preferences**. For checking WCAG violations, see
[Accessibility Issues](/core-concepts/issues#accessibility-issues).
## Best Practices
1. **Use Project Defaults**: Set a default device preset at the project level for consistent testing
2. **Match Target Devices**: Choose presets that match your target audience's devices
3. **Test Responsiveness**: Create presets for different device types to verify responsive design
4. **Document Custom Presets**: Add clear names and descriptions to custom presets
5. **Keep it Simple**: Use the default resolution and user agent unless you have specific requirements
## Restrictions
* Maximum viewport size: 1280x1024
* Device presets must use one of the three device types (Desktop/Tablet/Mobile)
* Each project can have only one default preset
## Troubleshooting
If you encounter issues with device presets:
1. **Preset Not Applied**: Check the priority order to understand which preset is being used
2. **Resolution Issues**: Make sure the override toggle is enabled if you want to use custom resolutions
3. **Custom Headers**: Verify header names and values are properly formatted
4. **Default Not Working**: Confirm only one preset is set as default in the project
## Technical Notes
* Device presets are stored as snapshots in test runs for reproducibility
* Default configurations are optimized for common testing scenarios
* Custom headers are preserved across test runs
* Locale and timezone settings affect date/time handling in tests
* Device preset information is stored in test results and can be used for analysis
## Related Documentation
* [Test Plans](/core-concepts/test-plans) - Configure device presets per test plan
* [Applications and Environments](/core-concepts/applications-and-environments) - How device presets fit into the test configuration hierarchy
* [AI Chat Assistant](/core-concepts/ai-chat-assistant) - Override device presets at runtime
* [GitHub App](/configuration/github-app) - Automatic device testing for PRs
# Dialog Handling
Source: https://docs.qa.tech/test-features/dialog-handling
How QA.tech handles browser dialogs (alerts, confirms, prompts) during automated testing
# Dialog Handling
QA.tech automatically handles browser dialogs that appear during test execution, ensuring your tests continue running smoothly without manual intervention.
## Supported Dialog Types
Our testing platform can capture and handle four types of browser dialogs:
**`window.alert()`** - Simple notification dialogs with just an "OK" button.
**Handled by default:** ✅ Yes - automatically accepted and text captured
**`window.confirm()`** - Dialogs with "OK" and "Cancel" options. **Handled by
default:** ❌ No - can be enabled on request
**`window.prompt()`** - Dialogs that request text input from the user.
**Handled by default:** ❌ No - can be enabled on request
**`window.onbeforeunload`** - Dialogs shown when leaving a page with unsaved
changes. **Handled by default:** ❌ No - can be enabled on request
## Default Behavior
### Alert Dialogs (Automatic)
By default, QA.tech automatically:
* **Captures** the alert message text
* **Accepts** the alert (clicks "OK")
* **Includes** the alert text in the test result
When an alert appears during a test action, agent will see output like:
```
Successfully clicked submit button. Alert(s) shown: alert: Please fill in all required fields
```
### Other Dialog Types (On Request)
Confirm, prompt, and beforeunload dialogs are not handled by default but can be configured if needed. Contact our support team if your application requires handling these dialog types.
## Why Browser Dialogs Should Be Avoided
**Avoid using `alert()`, `confirm()`, and `prompt()` dialogs in modern web
applications.**
All browser dialogs (`alert()`, `confirm()`, `prompt()`) are legacy APIs with significant limitations:
* **Synchronous Blocking** - All JavaScript execution halts while a dialog is open, blocking UI updates and network requests
* **Cannot be styled** - Appearance varies between browsers and cannot be customized to match your design system
* **Limited functionality** - Support only basic text and buttons, no rich content or custom button labels
* **Poor mobile experience** - May appear at OS level, breaking app visual flow and scaling inconsistently
* **Accessibility issues** - Screen readers and assistive technologies may not announce dialogs reliably
* **Testing complexity** - Require special handling in automation frameworks, interrupting test flows
## Modern Alternatives
Instead of browser dialogs, use these user-friendly patterns:
Build modals with your UI framework that match your design system, support
rich content, and include proper ARIA attributes for accessibility.
Embed validation errors or status messages directly adjacent to form fields,
providing real-time feedback without interruption.
Use non-blocking notification systems for success messages, warnings, and
informational alerts that don't halt user interaction.
For destructive actions, use confirmation screens or inline panels that show
contextual details and require explicit confirmation.
## Migration Strategy
If your application currently uses browser dialogs:
**Gradual Replacement Approach:** 1. **Audit** - Identify all occurrences of
`alert`, `confirm`, `prompt` in your codebase 2. **Prioritize** - Start with
high-traffic, user-facing areas first 3. **Build** - Develop reusable modal
and notification components 4. **Replace** - Swap native dialogs
incrementally, verifying behavior 5. **Test** - Ensure custom components work
with your automated tests
## Testing Dialog-Heavy Applications
If your application currently uses browser dialogs extensively:
**Contact Support:** Reach out to our team at
[hi@qa.tech](https://qa.tech/contact) to discuss: - Enabling additional dialog
types for your tests - Migration strategies for replacing dialogs with better
UX patterns - Custom handling for specific dialog scenarios
## Technical Implementation
When the AI agent encounters dialogs during test execution:
* **Alert dialogs** are automatically detected, captured, and accepted
* **Dialog text** is included in the test action results and visible in test traces
* **Other dialog types** (confirm, prompt, beforeunload) can be enabled for specific projects upon request
* **Test execution** continues seamlessly after dialog handling
## Best Practices
* Use `alert()` **only** for debugging/development - never in production -
Replace all browser dialogs with custom modals in production applications -
Describe dialog expectations clearly in your test goals (e.g., "expect a
success confirmation after form submission")
* Rely on any browser dialogs (`alert()`, `confirm()`, `prompt()`) for
production user interfaces - Use dialogs for complex user interactions -
Expect dialogs to work consistently across all devices and browsers
***
Need help with dialog handling in your tests? [Contact our support team](https://qa.tech/contact) for assistance.
# Dynamic Navigation
Source: https://docs.qa.tech/test-features/dynamic-navigation
How the AI agent navigates to URLs that change during test execution
The AI agent can navigate to URLs dynamically during test execution, without requiring static start URLs. This enables testing workflows where URLs are generated at runtime or vary based on test conditions.
## When to Use
Dynamic navigation is useful for testing:
* User-specific URLs (e.g., `/users/{dynamicId}/profile`)
* URLs displayed on the page that need to be visited
* URLs copied to the clipboard during the test
* Relative navigation within the same domain (e.g., `../settings`, `/dashboard`)
## Supported Navigation Modes
| Mode | Description | Example |
| :---------------- | :------------------------------------- | :--------------------------------------------- |
| **Full URL** | Navigate to complete web addresses | `https://example.com/page` |
| **Relative URL** | Navigate relative to current page | `/dashboard`, `../settings`, `?tab=profile` |
| **Clipboard URL** | Navigate to URL currently in clipboard | Copy URL with keyboard shortcut, then navigate |
## How It Works
The agent automatically handles navigation when:
1. **Your test instructions** mention navigating to a URL or visiting a link
2. **The agent encounters** a URL it needs to visit during test execution
3. **URLs are dynamic** - the agent adapts to whatever URL is present at runtime
**No configuration required** - the agent automatically determines which
navigation mode to use based on the URL format and current page context.
## Example Use Cases
**Goal:** "Navigate to the newly created user's profile page"
The agent will:
* Extract the dynamic user ID from the page
* Construct and navigate to the profile URL
* Verify the profile loads correctly
**Goal:** "Copy the verification link and visit it in the browser"
The agent will:
* Copy the link to the clipboard
* Navigate to the clipboard URL
* Verify the verification page loads
**Goal:** "From the product page, navigate to the shopping cart"
The agent will:
* Navigate relatively from current page (e.g., `../cart`)
* Handle relative paths without needing full URLs
## Technical Details
**Navigation Requirements:**
* **Full URLs**: Must use `http://`, `https://`, or `file://` protocol
* **Relative URLs**: Requires an active page (cannot navigate relatively from `about:blank`)
* **Clipboard URLs**: Clipboard must contain a valid web address
**Error Handling:**
The agent provides clear error messages when:
* Clipboard is empty when attempting clipboard navigation
* Relative URL is used without a current page loaded
* URL format is invalid or uses unsupported protocols
***
**Related:** See [Email Inbox](/test-features/email-inbox) for navigating to links in test emails.
# Email Inbox
Source: https://docs.qa.tech/test-features/email-inbox
Access and manage test emails during automated testing
During test execution, the AI agent has access to an email inbox that can receive and process emails. This is useful for testing features like:
* Account verification
* Password reset flows
* Email notifications
* Newsletter subscriptions
## How It Works
The inbox feature is powered by a direct integration with our email backend. When a test step requires checking email (e.g., "Wait for the welcome email"), the agent queries the inbox system directly.
* **Polling**: The agent polls for new emails for up to **3 minutes**. If no matching email is found within this time, the step fails.
* **No Pagination**: The system always looks for the **newest** email that matches your criteria. It does not support pagination or "get more" requests.
* **Text-Only View**: The agent sees a text summary of the email (subject, match reason, attachment summary), not the raw HTML or JSON.
### What the Agent Can Do
The agent interacts with the inbox in three specific ways:
| Action | Example Test Step | What the Agent Does |
| :----------------- | :---------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- |
| **Verify Receipt** | "Verify that a confirmation email was received" | Checks the inbox for a matching email and confirms its arrival. It sees the subject and a summary of attachments. |
| **Click Link** | "Click the 'Reset Password' link in the email" | Finds the newest matching email, locates the link described in your step, and navigates the browser to it. |
| **Get Code (OTP)** | "Get the verification code from the email" | Finds the email and extracts the One-Time Password (OTP) or verification code to use in the test. |
### Filtering & Matching
To find the right email, the system uses strict filtering and natural language matching:
* **Recipient**: Emails are strictly filtered by the `to_address`. The agent only sees emails sent to the specific address configured for the test.
* **Content Matching**: The agent uses your natural language query (e.g., "password reset email") to match against the subject, body, and attachments.
* **Time Window**: The system enforces a strict time cursor based on **when the test run started**.
* The agent can *only* see emails received **after** the test execution began.
* Emails received before the test started are invisible to the agent.
## Email Configs
QA.tech provides these system email configurations by default:
* **Single use Test Email Address** - New generated email for each test (can be re-used by having another test depend on the first)
* **Project e-mail address** - Static email always accessible for the project
**To use custom email addresses for testing:**
1. Navigate to Settings » Configs
2. Select "Email Inbox" type
3. Configure the email settings
4. Save the configuration
5. Add the config to your test case settings
### Supported Email Types
The inbox feature only works with emails managed by the QA.tech system.
| Config Type | Behavior | Inbox Access |
| :------------------- | :------------------------------------------------------------------ | :----------- |
| **Single Use** | Generates a unique email for **each test run**. Best for isolation. | ✅ Yes |
| **Fixed / Project** | Uses a stable email address. Good for whitelisting. | ✅ Yes |
| **Email + Password** | Generates a unique email **once per config**. | ✅ Yes |
| **External Email** | Using your own Gmail/Outlook address in a test step. | ❌ No |
**External emails are not supported.** The agent cannot access personal
inboxes like Gmail or Outlook. You must use a system-generated email config
for the agent to read emails.
### Whitelisting Email Addresses
If your application restricts which email addresses can sign up, receive mail, or pass spam filters, you need to allow QA.tech test addresses.
**Whitelist the `@qatech.email` domain.** All QA.tech-generated test addresses and all outgoing emails from QA.tech use this domain. Allowing the whole domain covers every address your tests may use, including:
* Project emails (`prj-xxxxx@qatech.email`)
* Magic link login emails (`magic-login-xxxxx@qatech.email`)
* Single-use addresses generated per test run (for example, `test-automation-abc123@qatech.email`)
Where to add the allowlist depends on your setup:
* **Signup or registration restrictions** — Allow `@qatech.email` so test accounts can be created.
* **Email server or gateway filters** — Allow inbound mail to `@qatech.email` so verification, password reset, and notification emails reach the test inbox.
* **Spam or security tools** — Allow outbound mail from `@qatech.email` so messages sent by QA.tech are not blocked or quarantined.
For a stable address you can add to a fixed allowlist, use a **Fixed /
Project** email config. Single-use addresses change on every run, so they are
harder to pre-approve.
## Using Email Inbox in Tests
1. Create or edit a test case
2. Under the Settings tab, select the email config to use
3. The test will now have access to the email inbox
## Manual Access
You can view test emails manually:
1. Go to Settings
2. Select Email Inbox
3. View all received test emails
## Important Considerations
* **Shared Inboxes**: If multiple tests use the same email inbox simultaneously (e.g., two password reset tests), emails may get mixed up. Use separate email configs for concurrent tests.
* **Sharing Emails Between Tests**: If Test B needs to check an email triggered by Test A:
* **Output Values**: In Test A, save the email address as an Output Value. In Test B, use that value to tell the agent which inbox to check.
* **Shared Configs**: If using a Fixed/Project email, both tests can use the same config. Be mindful of the time window (Test B must start *before* the email arrives, or you must trigger the email *during* Test B).
* **Cross-Test Limitations**: Because of the strict time window, a test cannot see emails that arrived *before* it started.
* If Test A triggers an email and finishes, and then Test B starts and tries to read that email, Test B will fail to find it.
* **Solution**: Combine the trigger and verification into the same test, or ensure the email is triggered *after* the verification test starts (e.g., using dependencies where the second test triggers the resend).
* **Timeout**: The agent will wait up to **3 minutes** for expected emails before failing.
* **Extending the wait**: You can add explicit "Wait" steps (e.g., "Wait 2 minutes") before the email check. Since the inbox cursor is fixed at the start of the test run, waiting *before* checking effectively extends the window for receiving emails.
* **Invitation Links** Since the browser state will be kept when opening the email you should log out after inviting a user to avoid that the invitation link redirects to the inviters account.
# File Downloads
Source: https://docs.qa.tech/test-features/file-downloads
Testing to export and download files
## Testing File Downloads
QA.tech's agent can test file download functionality in your web applications. When a file download is triggered during a test, the agent will:
1. Detect the download event
2. Wait for the download to complete
3. Verify the file was downloaded successfully
4. Show a success message with the file details
To test file downloads just write the test like you normally would and the agent will handle the rest.
For example *Click the export button* will automatically download the file and verify it was downloaded successfully.
### Download Limitations
* The agent will wait up to 30 seconds for a download to complete
* The agent will download files up to 100MB in size
* Some specialized download types (like streaming media) are not supported
* Downloaded files are just temporary and you can not upload the file again
* To test that a downloaded file follows a format please contact us and we can help
* Files can not be downloaded from emails
Contact us if you have questions or need to test this functionality
# File Uploads
Source: https://docs.qa.tech/test-features/file-uploads
Testing file uploads with default and custom files
This page covers how the AI agent handles file upload interactions **during test execution** - such as testing file input forms, drag-and-drop upload areas, or document import features in your application.
Looking to provide context documents to the AI during test creation? See
[Uploading Files and
Documents](/core-concepts/ai-chat-assistant#uploading-files-and-documents) for
uploading PDFs and specs to chat conversations.
## Default Test Files
QA.tech provides a set of default test files that are available for all tests:
| File | Type | Resolution | Good for |
| ------------------------ | ---- | ---------- | -------------------------- |
| `cat.pdf` | PDF | - | Document uploads |
| `cat.jpg` | JPEG | 612x408 | Generic image uploads |
| `frog-128x128.png` | PNG | 128x128 | Icons and small thumbnails |
| `owl-512x512.png` | PNG | 512x512 | Square avatars and logos |
| `elephant-1920x1080.jpg` | JPEG | 1920x1080 | Wide banners and cover art |
| `giraffe-1080x1920.jpg` | JPEG | 1080x1920 | Portrait and story formats |
Each image shows a different animal and prints its own resolution, so you can tell from a screenshot which file the agent uploaded.
These files are automatically available to the AI agent during test execution.
## Custom File Uploads
For testing specific file import features or custom file requirements, you can create file upload configs. Custom files are stored in Supabase storage and made available to the agent during test execution.
Go to **Settings → Configs** in your project dashboard
Click **Add config** and select **File Upload**
In the File Upload field, click to upload or drag and drop your test file
(max 250MB)
Enter a config name and click **Save**
Edit your test case, go to **Settings → Configs**, and select your file
upload config
### How Custom Files Work
When a test runs with a file upload config:
1. **File input elements**: When the agent clicks a file input (` `), a custom file chooser UI appears showing:
* Your custom uploaded files from configs
* The default test files if no custom files match
* Files are automatically filtered based on the input's `accept` attribute
2. **File selection**: The agent selects a file from the chooser UI, and the file is uploaded to your application
3. **File storage**: Custom files are stored in Supabase storage and downloaded to the browser session when needed
### Supported File Types
**Any file type is supported** - there are no MIME type or file extension restrictions. The File Upload config accepts files of any type up to 250MB in size. During test execution, files are filtered based on the HTML input element's `accept` attribute, so the agent will only use files that match your application's input restrictions.
### File Selector
Using javascript we catch the event that triggers a file selector and renders a custom file selector. This allows us to upload files from configs.
### Drag and Drop
The agent supports drag-and-drop file uploads for dropzone elements using the `selectFile` tool. When the agent encounters a dropzone (an element that accepts drag-and-drop events), it can dispatch a `drop` event with the file data.
**Current limitations:**
* Drag-and-drop support is currently limited to `cat.pdf` and `cat.jpg`
* Custom file uploads from configs work through file input elements (click to trigger the file chooser UI), not via drag-and-drop
* If you need to test drag-and-drop with custom files, use file input elements instead of dropzones
### File Type Restrictions
When your application has input restrictions (e.g., only accepting .csv files), our AI agent will automatically:
* Read the `accept` attribute from the HTML input element
* Filter available files to match the allowed MIME types, wildcards (e.g., `image/*`), or file extensions
* Only present files that match your application's restrictions in the file selector
This filtering ensures the agent only uses compatible files, even though the File Upload config accepts any file type.
# Mobile App Testing
Source: https://docs.qa.tech/test-features/mobile-app-testing
Test native iOS and Android apps with AI-powered automation
# Mobile App Testing
QA.tech supports testing native iOS and Android applications using the same AI agent that powers web testing. The agent interacts with your app through touch gestures, hardware controls, and deep links - covering the full range of user interactions on a mobile device.
## Network Access
QA.tech test runs originate from a fixed pool of IP addresses. If your app connects to a backend that has a **firewall, VPN gateway, IP allowlist, or rate-limiting rules**, you must whitelist these IPs to allow test traffic through.
Without whitelisting, your tests may fail with network errors, timeouts, or
unexpected login failures even though the app itself works fine.
### QA.tech IP Addresses
Find the current list of mobile testing IP ranges in the app under [**Settings → Network**](https://app.qa.tech/current-project/settings/network) — see the **Mobile Testing IP Whitelist** section.
Mobile testing uses a different set of IP ranges than web testing, so the
[Outbound IPs API](/api-reference/infrastructure/get-outbound-ips) does not
cover them. IP addresses may change — always use the live **Mobile Testing IP
Whitelist** in Settings → Network as the authoritative source.
### How to Add the Whitelist
Add the IP ranges from [Settings → Network](https://app.qa.tech/current-project/settings/network) to your security system's allowlist. The exact steps depend on your setup — see the [IP Access Control](/configuration/ip-access-control) guide for platform-specific instructions covering Cloudflare, AWS CloudFront, and other firewalls.
Common places to add these rules:
* Backend firewall or security group (AWS, GCP, Azure)
* API gateway rate-limiting or allowlist rules
* VPN / zero-trust gateway (e.g. Tailscale, Cloudflare Access, Zscaler)
* Mobile backend service IP allowlists
## Test Environments
Mobile tests run on cloud-hosted iOS Simulators and Android Emulators. Configure the simulator or emulator under **Settings → Device Presets** — see [Mobile Device Presets](/test-features/device-presets#mobile-device-presets) for device model, OS version, orientation, location, network log capture, and override priority.
| Environment | Status | Description |
| --------------- | ----------- | ------------------------------------------- |
| Cloud emulators | Available | iOS Simulator and Android Emulator in cloud |
| Real devices | Coming soon | Physical iOS and Android device testing |
## Debugging and Observability
| Feature | Status | Description |
| -------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Live view | Available | Watch tests run in real time from the test trace while a session is active |
| Network request logs | Available | Inspect HTTP requests in the test trace when network capture is enabled on the [device preset](/test-features/device-presets#mobile-device-presets) |
| Device console logs | Coming soon | App console output in the test trace |
## AI Chat and Test Dependencies
When mobile testing is enabled for your organization, you can create, edit, and run mobile tests through the [AI Chat Assistant](/core-concepts/ai-chat-assistant) — the same natural-language workflow as web testing.
For [test dependencies](/core-concepts/dependencies):
* **Wait For** — supported. Use it to control execution order between mobile tests.
* **Resume From** — not supported for mobile apps. Mobile tests cannot inherit app state from another test.
## Coming Soon
* Real device testing on physical iOS and Android hardware (selectable through device presets)
* Device console logs in the test trace
## Preparing Your App Build
Mobile testing requires a simulator or emulator build of your app, not an
AppStore or Play Store distribution build. Follow the steps below for your
platform.
Prepare an **iOS Simulator build** (`.app` file compressed as `.zip` or `.tar.gz`). AppStore distribution builds (`.ipa`) are not supported - they are device-specific and cannot run on simulators.
### Build with Xcode
Run and build your application in Xcode while targeting an iOS Simulator.
Once the build is complete and the app is running in the simulator, locate the `.app` file:
1. In Xcode, go to **Product** → **Show Build Folder in Finder**
2. Navigate to **Products/Debug-iphonesimulator/**
3. Find your `.app` file
### Build with Xcode Command Line Tools
```bash .xcodeproj theme={null}
xcodebuild -project '{project_name}.xcodeproj' \
-scheme '{scheme_name}' \
-sdk iphonesimulator \
-configuration Debug
```
```bash .xcworkspace theme={null}
xcodebuild -workspace '{your_workspace_name}.xcworkspace' \
-scheme '{scheme_name}' \
-sdk iphonesimulator \
-configuration Debug
```
The `.app` file is output to:
```
build/Debug-iphonesimulator/
```
### Compress the .app file
Once you have the `.app` file, compress it before uploading:
```bash theme={null}
zip -r AppName.zip AppName.app
```
Upload the resulting `.zip` (or `.tar.gz`) file - not the raw `.app` directory.
Prepare an **APK file** for your app. If your project produces an Android App Bundle (`.aab`), convert it to `.apk` first using the steps below.
### Build with Android Studio
Select **Build** → **Build APK(s)** → **Build APK(s)**
Once complete, click **locate** in the dialog that appears, or navigate to:
```
{project_name}/{app_module}/build/outputs/apk/
```
### Build with Gradle
Run the `assemble` command for your preferred build variant:
```bash theme={null}
./gradlew assembleDebug
```
The `.apk` file is output to:
```
{project_name}/{app_module}/build/outputs/apk/
```
### Convert AAB to APK
If you only have an Android App Bundle (`.aab`), convert it using [bundletool](https://developer.android.com/tools/bundletool):
```bash theme={null}
# Generate a universal APK set
bundletool build-apks --bundle=/{your_app}/{name}.aab \
--output=/{your_app}/{name}.apks \
--mode=universal
# Extract a single APK from the set
unzip -p /{your_app}/{name}.apks universal.apk > /{your_app}/{name}.apk
```
Upload the resulting `.apk` file.
## Setting Up a Mobile App in QA.tech
In your project, go to **Settings → Applications & Envs** and create a new
application. Select **Mobile App** as the application type.
Add an environment to your mobile application. Select the platform - **iOS** or **Android** - and give the environment a name (for example: Staging or Production).
No URL is required for mobile apps.
Upload your app build file:
* iOS: `.zip` or `.tar.gz` containing your `.app` simulator build (max 4 GB)
* Android: `.apk` file (max 4 GB)
Create tests for your mobile application. The AI agent will interact with your app using touch gestures and device controls.
## AI Agent Capabilities
The agent interacts with your app using the following actions:
### Touch Interactions
| Action | Description |
| -------------- | ----------------------------------------------------- |
| Tap | Tap at a specific point on the screen |
| Double tap | Double-tap at a specific point |
| Long press | Press and hold at a point for a configurable duration |
| Swipe | Swipe from one point to another |
| Type | Tap a field and type text |
| Clear and type | Clear an existing field value, then type |
### Device Controls
| Action | Platforms | Description |
| -------------- | ------------ | ------------------------------------------ |
| Home button | iOS, Android | Press the device home button |
| Back button | Android only | Press the Android back button |
| App switcher | iOS, Android | Open the app switcher |
| Launch app | iOS, Android | Launch an app by bundle ID or package name |
| Close app | iOS, Android | Close the current app |
| Open URL | iOS, Android | Open a URL or deep link |
| Rotate | Android only | Switch between portrait and landscape |
| Volume up/down | iOS, Android | Press hardware volume keys |
## Requirements
Mobile testing is enabled per organization. Contact [support](mailto:hi@qa.tech) to enable mobile testing for your account.
# Revision History
Source: https://docs.qa.tech/test-features/revision-history
View and compare previous versions of your test cases
Revision history automatically tracks every change you make to your test cases. Each time you edit a test case, a new version is created, preserving a complete history of changes. You can view what changed between versions, see who made each change, and navigate to any previous version.
## Overview
Every edit to a test case creates a new revision, allowing you to track changes over time. Revision history helps you:
* See what changed between versions
* Understand when and who made changes
* Navigate to previous versions of your test case
* Compare revisions side-by-side
## Accessing Revision History
Navigate to your test case and click to edit it
Click the "Version History" button or menu item in the test case editor
The Version History panel opens, showing all previous versions of the test
case, ordered from newest to oldest
## Understanding Revisions
Each revision in the history shows:
* **Revision ID**: A shortened identifier for the version
* **Badges**:
* **Current**: The active version of the test case (blue badge)
* **Draft**: The latest version, if different from current (gray badge)
* **Date**: When the revision was created (relative time, e.g., "2 hours ago")
* **Creator**: The person who made the change (display name or email)
* **View Changes**: Button to compare this revision with the previous one
### What Gets Tracked
Revisions track changes to test case content:
| Tracked in Revisions | Not Tracked |
| -------------------- | ------------------------- |
| Steps | Test case name |
| Prompt examples | Classification |
| Prompt goal | Status (enabled/disabled) |
| Required configs | Labels |
| Start URL path | |
| Agent selection | |
Test case metadata (name, classification, status) is stored separately and not included in revision history.
## Working with Revisions
Click the "View Changes" button to compare revisions side-by-side, or click any revision ID to navigate to that version's edit view. When viewing an older version, banners indicate the revision state and provide quick navigation back to the current version.
## Limitations
* **Revisions cannot be deleted**: All revisions are preserved as historical records
* **No restore button**: To restore an old version, navigate to it and make edits from there
* **Diff shows JSON**: The change comparison displays JSON structure, not formatted test case steps