API Reference
Complete reference for the Qualigate REST API. Trigger tests, check status, retrieve results, and configure CI/CD integrations programmatically.
Authentication
All API requests require authentication using an API key. You can pass the key using either header format:
# Option 1: Authorization header (recommended)
curl -H "Authorization: Bearer qg_your_api_key_here" \
https://qualigate.app/api/v1/trigger
# Option 2: X-API-Key header
curl -H "X-API-Key: qg_your_api_key_here" \
https://qualigate.app/api/v1/triggerGenerating API Keys
Go to Settings > API Keys in your dashboard. Click Generate New Key, name it descriptively (e.g., "GitHub Actions CI"), and copy the key. Keys start with qg_ and are only shown once.
Never expose API keys in client-side code, commit them to version control, or include them in URLs. Store keys in environment variables or your CI/CD platform's secret management.
Base URL
https://qualigate.appAll endpoints below are relative to this base URL. Use HTTPS for all requests.
/api/v1/trigger
Trigger one or more test runs. You must provide exactly one of test_case_id, test_suite_id, or project_id.
Request Body
Required (one of):
| Parameter | Type | Description |
|---|---|---|
test_case_id | UUID | Run a single test case |
test_suite_id | UUID | Run all test cases in a suite |
project_id | UUID | Run all test cases in a project |
Optional:
| Parameter | Type | Default | Description |
|---|---|---|---|
browserType | string | "chromium" | Browser engine to use |
viewportPreset | string | "desktop" | "desktop" | "tablet" | "mobile" |
viewportWidth | number | — | Custom viewport width in pixels |
viewportHeight | number | — | Custom viewport height in pixels |
context | object | — | CI/CD metadata (see below) |
Context Object:
Optional metadata from your CI/CD pipeline. All fields are strings and appear in the Qualigate dashboard for traceability.
| Field | Description |
|---|---|
source | CI provider name (e.g., "github-actions", "jenkins") |
pr_number | Pull request number |
pr_url | Pull request URL |
sha | Git commit SHA |
branch | Git branch name |
repository | Repository name (e.g., "org/repo") |
Examples
Run a Single Test Case
curl -X POST https://qualigate.app/api/v1/trigger \
-H "Authorization: Bearer qg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"test_case_id": "550e8400-e29b-41d4-a716-446655440000",
"browserType": "chromium",
"viewportPreset": "desktop",
"context": {
"source": "github-actions",
"pr_number": "42",
"branch": "main",
"sha": "abc123def456"
}
}'Response (201 Created)
{
"success": true,
"test_run_id": "660e8400-e29b-41d4-a716-446655440000",
"status": "pending",
"message": "Test run queued successfully"
}Run All Tests in a Suite
curl -X POST https://qualigate.app/api/v1/trigger \
-H "Authorization: Bearer qg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"test_suite_id": "770e8400-e29b-41d4-a716-446655440000"
}'Response (201 Created)
{
"success": true,
"suite_run_id": "880e8400-e29b-41d4-a716-446655440000",
"test_count": 5,
"status": "pending",
"message": "5 tests queued successfully"
}Run All Tests in a Project
curl -X POST https://qualigate.app/api/v1/trigger \
-H "Authorization: Bearer qg_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"project_id": "990e8400-e29b-41d4-a716-446655440000"
}'Response (201 Created)
{
"success": true,
"test_run_ids": [
"aa0e8400-e29b-41d4-a716-446655440000",
"bb0e8400-e29b-41d4-a716-446655440000",
"cc0e8400-e29b-41d4-a716-446655440000"
],
"test_count": 3,
"status": "pending",
"message": "3 tests queued successfully"
}JavaScript / Node.js Example
const response = await fetch("https://qualigate.app/api/v1/trigger", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.QUALIGATE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
test_case_id: "550e8400-e29b-41d4-a716-446655440000",
viewportPreset: "desktop",
context: {
source: "custom-script",
branch: "main",
},
}),
});
const data = await response.json();
console.log("Test run ID:", data.test_run_id);Python Example
import os
import requests
response = requests.post(
"https://qualigate.app/api/v1/trigger",
headers={
"Authorization": f"Bearer {os.environ['QUALIGATE_API_KEY']}",
"Content-Type": "application/json",
},
json={
"test_suite_id": "770e8400-e29b-41d4-a716-446655440000",
"context": {
"source": "python-script",
"branch": "develop",
},
},
)
data = response.json()
print(f"Suite run ID: {data['suite_run_id']}")
print(f"Tests queued: {data['test_count']}")/api/v1/trigger
Check the status of a running or completed test. Poll this endpoint after triggering a test to determine when it finishes.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
test_run_id | UUID | Check a single test run status |
suite_run_id | UUID | Check a suite run status (aggregated across all tests) |
Examples
Check Single Test Run
curl -H "Authorization: Bearer qg_your_api_key" \
"https://qualigate.app/api/v1/trigger?test_run_id=660e8400-e29b-41d4-a716-446655440000"Response (200 OK)
{
"id": "660e8400-e29b-41d4-a716-446655440000",
"status": "passed",
"started_at": "2026-01-28T10:30:00Z",
"completed_at": "2026-01-28T10:32:15Z",
"duration_ms": 135000,
"steps_completed": 12,
"steps_total": 12,
"error_message": null,
"video_url": "https://nnkppgzxyitzkfntrthf.supabase.co/storage/v1/object/public/recordings/..."
}Check Suite Run
curl -H "Authorization: Bearer qg_your_api_key" \
"https://qualigate.app/api/v1/trigger?suite_run_id=880e8400-e29b-41d4-a716-446655440000"Response (200 OK)
{
"id": "880e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"started_at": "2026-01-28T10:30:00Z",
"completed_at": "2026-01-28T10:35:00Z",
"duration_ms": 300000,
"total_tests": 5,
"passed_tests": 4,
"failed_tests": 1
}Polling Script (Bash)
#!/bin/bash
RUN_ID="$1"
API_KEY="$QUALIGATE_API_KEY"
for i in $(seq 1 60); do
RESULT=$(curl -s -H "Authorization: Bearer $API_KEY" \
"https://qualigate.app/api/v1/trigger?test_run_id=$RUN_ID")
STATUS=$(echo $RESULT | jq -r '.status')
echo "[$i] Status: $STATUS"
case "$STATUS" in
passed) echo "Test passed!"; exit 0 ;;
failed|error|timeout) echo "Test $STATUS"; exit 1 ;;
esac
sleep 10
done
echo "Timed out after 10 minutes"
exit 1/api/v1/results
Get aggregated test results for multiple test runs or a suite. Useful for generating CI reports or dashboard summaries.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
test_run_ids | string | Comma-separated list of test run UUIDs |
suite_run_id | UUID | Suite run ID to get all results for |
Examples
Get Multiple Test Results
curl -H "Authorization: Bearer qg_your_api_key" \
"https://qualigate.app/api/v1/results?test_run_ids=aa0e8400-e29b-41d4-a716-446655440000,bb0e8400-e29b-41d4-a716-446655440000"Response (200 OK)
{
"total_tests": 2,
"passed_tests": 1,
"failed_tests": 1,
"status": "failed",
"results": [
{
"test_run_id": "aa0e8400-e29b-41d4-a716-446655440000",
"test_case_name": "User Login Flow",
"status": "passed",
"duration_ms": 45000,
"dashboard_url": "https://qualigate.app/test-runs/aa0e8400-e29b-41d4-a716-446655440000"
},
{
"test_run_id": "bb0e8400-e29b-41d4-a716-446655440000",
"test_case_name": "Checkout Process",
"status": "failed",
"duration_ms": 60000,
"error_message": "Element not found: button[data-test='submit-order']",
"dashboard_url": "https://qualigate.app/test-runs/bb0e8400-e29b-41d4-a716-446655440000"
}
]
}Get Suite Results
curl -H "Authorization: Bearer qg_your_api_key" \
"https://qualigate.app/api/v1/results?suite_run_id=880e8400-e29b-41d4-a716-446655440000"Response (200 OK)
{
"suite_run_id": "880e8400-e29b-41d4-a716-446655440000",
"suite_name": "Critical User Flows",
"total_tests": 5,
"passed_tests": 4,
"failed_tests": 1,
"status": "failed",
"results": [
{
"test_run_id": "cc0e8400-e29b-41d4-a716-446655440000",
"test_case_name": "User Login",
"status": "passed",
"duration_ms": 30000,
"dashboard_url": "https://qualigate.app/test-runs/cc0e8400-e29b-41d4-a716-446655440000"
}
]
}/api/v1/ci-config
Retrieve the CI/CD configuration for a project, including the trigger settings and associated test suite.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
project_id | UUID | The project to retrieve CI configuration for |
Example
curl -H "Authorization: Bearer qg_your_api_key" \
"https://qualigate.app/api/v1/ci-config?project_id=990e8400-e29b-41d4-a716-446655440000"Response (200 OK)
{
"project_id": "990e8400-e29b-41d4-a716-446655440000",
"project_name": "E-commerce Site",
"ci_config": {
"provider": "github-actions",
"trigger_on": ["push", "pull_request"],
"branches": ["main", "develop"],
"test_suite_id": "770e8400-e29b-41d4-a716-446655440000"
}
}Rate Limits
| Endpoint | Limit | Window |
|---|---|---|
/api/v1/trigger (POST) | 30 requests | Per minute, per API key |
/api/v1/trigger (GET) | 30 requests | Per minute, per API key |
/api/v1/results | 30 requests | Per minute, per API key |
When you exceed the rate limit, the API returns a 429 Too Many Requests response with a retry_after field indicating how many seconds to wait. Implement exponential backoff in your polling logic.
Status Values
| Status | Terminal? | Description |
|---|---|---|
pending | No | Test is queued and waiting to start |
running | No | Test is currently executing in the browser |
passed | Yes | All steps and assertions completed successfully |
failed | Yes | An assertion failed or a required element was not found |
error | Yes | An unexpected error occurred (browser crash, network failure) |
timeout | Yes | Test exceeded the maximum execution time |
canceled | Yes | Test was manually canceled by a user |
Keep polling while status is pending or running. Stop when a terminal status is reached.
Error Responses
All error responses use a consistent JSON format:
{
"error": "Error Type",
"message": "Human-readable description of what went wrong"
}401 Unauthorized
Missing or invalid API key. Ensure the key is active and correctly formatted in the Authorization or X-API-Key header.
{
"error": "Unauthorized",
"message": "Missing or invalid API key"
}403 Forbidden
The API key is valid but does not have access to the requested resource. Check that the test case, suite, or project belongs to the same account.
{
"error": "Forbidden",
"message": "You don't have access to this resource"
}404 Not Found
The specified test case, suite, project, or test run does not exist. Verify the UUID is correct.
{
"error": "Not Found",
"message": "Test case not found"
}429 Too Many Requests
Rate limit exceeded. Wait the number of seconds specified in retry_after before retrying.
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Please try again later.",
"retry_after": 60
}500 Internal Server Error
An unexpected server error. Retry with exponential backoff. If the issue persists, contact support.
{
"error": "Internal Server Error",
"message": "An unexpected error occurred. Please try again later."
}Best Practices
1. Poll with Appropriate Intervals
After triggering a test, poll the status endpoint every 10 seconds. Most tests complete in 1--3 minutes. Set a maximum timeout of 10 minutes and handle the timeout case in your pipeline.
2. Implement Exponential Backoff
When you receive a 429 response, use the retry_after value. For 500 errors, start with a 1-second delay and double it on each retry up to a maximum of 30 seconds.
3. Include CI Context
Always pass the context object with branch, SHA, and PR number. This metadata appears in the Qualigate dashboard and makes it easy to trace test failures back to the specific commit or pull request that caused them.
4. Secure Your API Keys
Store API keys in environment variables or secret management systems (GitHub Secrets, AWS Secrets Manager, Vault). Use separate keys for each environment (dev, staging, production) so you can rotate them independently.
5. Fail Builds on Test Failure
In CI/CD pipelines, exit with a non-zero code when tests fail. This prevents broken code from being deployed. Check the status field for "failed", "error", or "timeout" values.
Quick Start Examples
Complete working examples for common integration scenarios.
GitHub Actions Workflow
Add this to .github/workflows/e2e.yml in your repository. Set QUALIGATE_API_KEY as a repository secret and QUALIGATE_SUITE_ID as a variable.
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- name: Trigger Qualigate Tests
id: trigger
run: |
RESPONSE=$(curl -s -X POST https://qualigate.app/api/v1/trigger \
-H "Authorization: Bearer ${{ secrets.QUALIGATE_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"test_suite_id": "${{ vars.QUALIGATE_SUITE_ID }}",
"context": {
"source": "github-actions",
"branch": "${{ github.ref_name }}",
"sha": "${{ github.sha }}",
"pr_number": "${{ github.event.pull_request.number }}",
"repository": "${{ github.repository }}"
}
}')
echo "run_id=$(echo $RESPONSE | jq -r '.suite_run_id // .test_run_id')" >> $GITHUB_OUTPUT
- name: Wait for Results
run: |
for i in $(seq 1 60); do
RESULT=$(curl -s \
-H "Authorization: Bearer ${{ secrets.QUALIGATE_API_KEY }}" \
"https://qualigate.app/api/v1/trigger?suite_run_id=${{ steps.trigger.outputs.run_id }}")
STATUS=$(echo $RESULT | jq -r '.status')
echo "Attempt $i: $STATUS"
if [ "$STATUS" = "completed" ] || [ "$STATUS" = "passed" ]; then
exit 0
elif [ "$STATUS" = "failed" ]; then
echo "Tests failed"
exit 1
fi
sleep 10
done
echo "Timed out"
exit 1Node.js Script (Trigger + Poll)
async function runE2ETests(suiteId: string): Promise<boolean> {
const API_KEY = process.env.QUALIGATE_API_KEY!;
const BASE = "https://qualigate.app";
// 1. Trigger
const triggerRes = await fetch(`${BASE}/api/v1/trigger`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ test_suite_id: suiteId }),
});
const { suite_run_id } = await triggerRes.json();
console.log(`Suite run started: ${suite_run_id}`);
// 2. Poll
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 10000));
const statusRes = await fetch(
`${BASE}/api/v1/trigger?suite_run_id=${suite_run_id}`,
{ headers: { Authorization: `Bearer ${API_KEY}` } }
);
const data = await statusRes.json();
console.log(`[${i + 1}] Status: ${data.status}`);
if (data.status === "completed" || data.status === "passed") {
console.log(`Passed: ${data.passed_tests}/${data.total_tests}`);
return true;
}
if (data.status === "failed") {
console.log(`Failed: ${data.failed_tests}/${data.total_tests}`);
return false;
}
}
throw new Error("Timed out waiting for test results");
}
// Usage
runE2ETests("your-suite-uuid")
.then((ok) => process.exit(ok ? 0 : 1))
.catch(() => process.exit(1));Need Help?
If you have questions or need assistance with the API:
- Read the Getting Started guide for a step-by-step walkthrough
- See the GitHub Actions guide for CI/CD integration examples
- Read CI/CD Integration Guide on the blog for best practices
- Contact support at support@qualigate.app