Overview
The QualGent API enables you to manage and execute mobile app testing workflows directly from your CI/CD pipeline.
Key Features
π Test Execution
Run individual test cases or execute your entire test suite against uploaded applications
π CI/CD Integration
Seamlessly integrate automated testing into your deployment pipeline
π Job Monitoring
Track test run status, progress, and retrieve detailed results
π± Multi-platform Support
Test on both Android and iOS devices with flexible device configuration
API Workflow
The QualGent API is a RESTful service built on FastAPI that orchestrates automated mobile testing workflows. All data is organization-scoped for multi-tenant isolation, with authentication enforced via API key headers.
Upload Your Application
Submit your mobile application using the Apps API. The upload endpoint automatically handles large files by chunking them server-side, so you can upload files of any size with a single request.
Run Your Tests
Queue test runs against your uploaded application using the Test Cases API. Execute individual test cases or run your entire test suite in a single batch request with up to 10 parallel tests.
Get Results
Monitor test execution in real-time through the Jobs API. Track progress, view status updates, and retrieve comprehensive test reports including pass/fail status and detailed execution logs.
Quickstart
Get started with the QualGent API for automated mobile testing.
Before you begin
This guide walks you through a simple interaction with the QualGent APIβuploading a test application and running your first test case. For a complete understanding of QualGent API objects and how they fit together, visit the API reference.
Set up your development environment
Before making API requests, ensure you have the necessary tools:
- HTTP Client - Use curl (command line), Postman, or an HTTP library in your preferred language (requests for Python, fetch for Node.js, etc.).
- Base URL - All API requests should be made to
http://api.qualgent.ai - Test Application - Prepare a mobile application file (Android or iOS) that you want to test.
Send your first API request
Let's start by listing all available test cases in your organization.
curl http://api.qualgent.ai/v1/test-cases/list \
-H "X-Api-Key: qg_your_api_key_here"
import requests
response = requests.get(
'http://api.qualgent.ai/v1/test-cases/list',
headers={'X-Api-Key': 'qg_your_api_key_here'}
)
test_cases = response.json()
print(test_cases)
const fetch = require('node-fetch');
const response = await fetch('http://api.qualgent.ai/v1/test-cases/list', {
headers: {
'X-Api-Key': 'qg_your_api_key_here'
}
});
const testCases = await response.json();
console.log(testCases);
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
client := &http.Client{}
req, _ := http.NewRequest("GET", "http://api.qualgent.ai/v1/test-cases/list", nil)
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
If everything worked, you'll receive a JSON response with your organization's test cases.
[
{
"id": "tc_1234567890",
"name": "Login Flow Test",
"status": "active",
"category": {
"id": "cat_abc123",
"name": "Smoke Tests"
},
"latest_completed_run": {
"id": "run_abc123def456",
"link": "https://app.qualgent.ai/test-runs/run_abc123def456",
"app": {
"id": "file_xyz789",
"name": "MyApp v1.0.0"
},
"status": "passed",
"priority": "medium",
"progress": 100,
"executed_by": {
"id": "user_abc123",
"name": "John Doe",
"email": "john@example.com"
},
"device": "iPhone 14 Pro"
}
}
]
Authentication
Authenticate your API requests by including your API key in the request header.
Getting Your API Key
If you don't already have a QualGent API key, you'll need to find it in your Dashboard. Follow these steps to get started:
- Create an account at qualgent.ai/signup
- From your Dashboard, navigate to Settings, then to the Developer tab to access API key management
- Here you can generate a new API key, copy an existing key, or revoke them as needed
Authentication Header
| Header | Value | Description |
|---|---|---|
X-Api-Key |
qg_your_api_key |
Your QualGent API key |
Storing Your API Key Securely
Never hardcode your API key directly in source code. Use environment variables to keep credentials secure:
# Set environment variable
export QUALGENT_API_KEY="qg_your_api_key_here"
# Use in curl commands
curl http://api.qualgent.ai/v1/apps \
-H "X-Api-Key: $QUALGENT_API_KEY"
import os
from dotenv import load_dotenv
# Load from .env file
load_dotenv()
api_key = os.getenv('QUALGENT_API_KEY')
# Use in requests
import requests
response = requests.get(
'http://api.qualgent.ai/v1/apps',
headers={'X-Api-Key': api_key}
)
// Load from .env file
require('dotenv').config();
const apiKey = process.env.QUALGENT_API_KEY;
// Use in fetch requests
const response = await fetch('http://api.qualgent.ai/v1/apps', {
headers: {
'X-Api-Key': apiKey
}
});
package main
import (
"net/http"
"os"
)
func main() {
// Load from environment variable
apiKey := os.Getenv("QUALGENT_API_KEY")
// Use in HTTP requests
client := &http.Client{}
req, _ := http.NewRequest("GET", "http://api.qualgent.ai/v1/apps", nil)
req.Header.Add("X-Api-Key", apiKey)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
Create a .env file in your project root with QUALGENT_API_KEY=qg_your_api_key_here and add it to .gitignore.
Rate Limits
The QualGent API enforces rate limits to ensure fair usage and system stability. Rate limits are applied per API key.
Rate Limit Details
| Limit | Description |
|---|---|
10 requests/second |
Maximum number of requests allowed per second per API key |
When you make API requests, the response includes rate limit headers that indicate your current usage:
X-RateLimit-Limit- The maximum number of requests allowed per secondX-RateLimit-Remaining- The number of requests remaining in the current windowX-RateLimit-Reset- The time (in seconds) until the rate limit resets
If you exceed the rate limit, you'll receive a 429 Too Many Requests response. The response will include a Retry-After header indicating how long to wait before making another request.
Managing Your Applications
Uploading an App
Upload the mobile application that you want to run tests on:
curl http://api.qualgent.ai/v1/apps/upload \
-H "X-Api-Key: qg_your_api_key_here" \
-F "file=@/path/to/your/app.apk" \
-F "app_name=MyApp" \
-F "version=1.0.0"
import requests
with open('/path/to/your/app.apk', 'rb') as f:
files = {'file': f}
data = {
'app_name': 'MyApp',
'version': '1.0.0',
'os': 'android' # Optional: auto-detected from file extension if not provided
}
response = requests.post(
'http://api.qualgent.ai/v1/apps/upload',
headers={'X-Api-Key': 'qg_your_api_key_here'},
files=files,
data=data
)
print(response.json())
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');
const form = new FormData();
form.append('file', fs.createReadStream('/path/to/your/app.apk'));
form.append('app_name', 'MyApp');
form.append('version', '1.0.0');
form.append('os', 'android'); // Optional: auto-detected from file extension if not provided
const response = await fetch('http://api.qualgent.ai/v1/apps/upload', {
method: 'POST',
headers: {
'X-Api-Key': 'qg_your_api_key_here',
...form.getHeaders()
},
body: form
});
const result = await response.json();
console.log(result);
package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
file, _ := os.Open("/path/to/your/app.apk")
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, _ := writer.CreateFormFile("file", "app.apk")
io.Copy(part, file)
writer.WriteField("app_name", "MyApp")
writer.WriteField("version", "1.0.0")
writer.WriteField("os", "android") // Optional: auto-detected from file extension if not provided
writer.Close()
req, _ := http.NewRequest("POST", "http://api.qualgent.ai/v1/apps/upload", body)
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
req.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
fmt.Println(string(respBody))
}
If the upload succeeds, you'll receive file metadata.
{
"success": true,
"file": {
"file_path": "user_abc123/1757339253852-MyApp-1.0.0.apk",
"filename": "1757339253852-MyApp-1.0.0.apk",
"original_name": "app.apk",
"file_size": 52428800,
"file_type": "application/vnd.android.package-archive",
"app_name": "MyApp",
"version": "1.0.0",
"os": "android",
"user_id": "user_abc123",
"organization_id": "org_xyz789"
}
}
Handling Test Cases
Once you've uploaded your application, you can run test cases against it.
Run by Test Case ID
Queue test runs by providing the test case IDs you want to execute:
curl http://api.qualgent.ai/v1/test-cases/run \
-H "X-Api-Key: qg_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"jobs": [
{
"device": {
"name": "Pixel 7",
"platform": "Android",
"os_version": "14",
"orientation": "portrait"
},
"test_case_id": "tc_xyz789",
"app_file_id": "file_abc123",
}
]
}'
import requests
response = requests.post(
'http://api.qualgent.ai/v1/test-cases/run',
headers={
'X-Api-Key': 'qg_your_api_key_here',
'Content-Type': 'application/json'
},
json={
'jobs': [
{
'test_case_id': 'tc_xyz789',
'platform': 'android', # Optional: for auto-selection
'app_file_id': 'file_abc123', # Optional if platform provided
'device': { # Optional if platform provided
'name': 'Pixel 7',
'platform': 'Android',
'os_version': '14',
'orientation': 'portrait'
}
}
]
}
)
print(response.json())
const fetch = require('node-fetch');
const response = await fetch('http://api.qualgent.ai/v1/test-cases/run', {
method: 'POST',
headers: {
'X-Api-Key': 'qg_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
jobs: [
{
test_case_id: 'tc_xyz789',
platform: 'android', // Optional: for auto-selection
app_file_id: 'file_abc123', // Optional if platform provided
device: { // Optional if platform provided
name: 'Pixel 7',
platform: 'Android',
os_version: '14',
orientation: 'portrait'
}
}
]
})
});
const result = await response.json();
console.log(result);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
payload := map[string]interface{}{
"jobs": []map[string]interface{}{
{
"test_case_id": "tc_xyz789",
"platform": "android", // Optional: for auto-selection
"app_file_id": "file_abc123", // Optional if platform provided
"device": map[string]interface{}{ // Optional if platform provided
"name": "Pixel 7",
"platform": "Android",
"os_version": "14",
"orientation": "portrait",
},
},
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "http://api.qualgent.ai/v1/test-cases/run", bytes.NewBuffer(jsonData))
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
{
"success": true,
"jobs": [
{
"link": "https://app.qualgent.ai/test-runs/run_abc123def456"
}
]
}
Run all tests
Execute your complete test suite against a specified application. Optional filters allow you to narrow execution scope, such as by category, enabling targeted regression testing and optimized resource allocation.
curl http://api.qualgent.ai/v1/test-cases/run-all \
-H "X-Api-Key: qg_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"device": {
"name": "Pixel 7",
"platform": "Android",
"os_version": "14",
"orientation": "portrait"
},
"app_file_id": "file_abc123",
"category_id": "770e8400-e29b-41d4-a716-446655440000"
}'
import requests
response = requests.post(
'http://api.qualgent.ai/v1/test-cases/run-all',
headers={
'X-Api-Key': 'qg_your_api_key_here',
'Content-Type': 'application/json'
},
json={
'app_file_id': 'file_abc123',
'device': {
'name': 'Pixel 7',
'platform': 'Android',
'os_version': '14',
'orientation': 'portrait'
},
'category_id': '770e8400-e29b-41d4-a716-446655440000' # Optional
}
)
print(response.json())
const fetch = require('node-fetch');
const response = await fetch('http://api.qualgent.ai/v1/test-cases/run-all', {
method: 'POST',
headers: {
'X-Api-Key': 'qg_your_api_key_here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
app_file_id: 'file_abc123',
device: {
name: 'Pixel 7',
platform: 'Android',
os_version: '14',
orientation: 'portrait'
},
category_id: '770e8400-e29b-41d4-a716-446655440000' // Optional
})
});
const result = await response.json();
console.log(result);
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
payload := map[string]interface{}{
"app_file_id": "file_abc123",
"device": map[string]interface{}{
"name": "Pixel 7",
"platform": "Android",
"os_version": "14",
"orientation": "portrait",
},
"category_id": "770e8400-e29b-41d4-a716-446655440000", // Optional
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "http://api.qualgent.ai/v1/test-cases/run-all", bytes.NewBuffer(jsonData))
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
{
"success": true,
"jobs": [
{
"link": "https://app.qualgent.ai/test-runs/run_abc123"
},
{
"link": "https://app.qualgent.ai/test-runs/run_def456"
},
{
"link": "https://app.qualgent.ai/test-runs/run_ghi789"
}
]
}
Monitor Jobs
After queuing test runs, you can monitor their status and retrieve results.
List all jobs
curl http://api.qualgent.ai/v1/jobs/list \
-H "X-Api-Key: qg_your_api_key_here"
import requests
response = requests.get(
'http://api.qualgent.ai/v1/jobs/list',
headers={'X-Api-Key': 'qg_your_api_key_here'}
)
jobs = response.json()
print(jobs)
const fetch = require('node-fetch');
const response = await fetch('http://api.qualgent.ai/v1/jobs/list', {
headers: {
'X-Api-Key': 'qg_your_api_key_here'
}
});
const jobs = await response.json();
console.log(jobs);
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
client := &http.Client{}
req, _ := http.NewRequest("GET", "http://api.qualgent.ai/v1/jobs/list", nil)
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
{
"test_runs": [
{
"id": "run_abc123",
"link": "https://app.qualgent.ai/test-runs/run_abc123",
"test_case": {
"id": "tc_1234567890",
"name": "Login Flow Test"
},
"app": {
"id": "file_xyz789",
"name": "MyApp v1.0.0"
},
"status": "completed",
"priority": "medium",
"progress": 100,
"executed_by": {
"name": "John Doe",
"email": "john@example.com"
},
"device": "iPhone 14 Pro"
},
{
"id": "run_def456",
"link": "https://app.qualgent.ai/test-runs/run_def456",
"test_case": {
"id": "tc_9876543210",
"name": "Payment Flow Test"
},
"app": {
"id": "file_xyz789",
"name": "MyApp v1.0.0"
},
"status": "running",
"priority": "high",
"progress": 45,
"executed_by": {
"name": "Jane Smith",
"email": "jane@example.com"
},
"device": "Pixel 7"
}
]
}
Check Job Status
curl http://api.qualgent.ai/v1/jobs/status/run_abc123 \
-H "X-Api-Key: qg_your_api_key_here"
import requests
response = requests.get(
'http://api.qualgent.ai/v1/jobs/status/run_abc123',
headers={'X-Api-Key': 'qg_your_api_key_here'}
)
job_status = response.json()
print(f"Status: {job_status['status']}")
const fetch = require('node-fetch');
const response = await fetch('http://api.qualgent.ai/v1/jobs/status/run_abc123', {
headers: {
'X-Api-Key': 'qg_your_api_key_here'
}
});
const jobStatus = await response.json();
console.log('Status:', jobStatus.status);
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
client := &http.Client{}
req, _ := http.NewRequest("GET", "http://api.qualgent.ai/v1/jobs/status/run_abc123", nil)
req.Header.Add("X-Api-Key", "qg_your_api_key_here")
resp, _ := client.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
{
"id": "run_abc123",
"test_case": {
"id": "tc_1234567890",
"name": "Login Flow Test"
},
"app": {
"id": "file_xyz789",
"name": "MyApp v1.0.0"
},
"status": "completed|failed|queued|running",
"priority": "medium",
"progress": 100,
"executed_by": {
"id": "user_abc123",
"name": "John Doe",
"email": "john@example.com"
},
"device": "iPhone 14 Pro"
}
Apps API
Upload App
Upload an application file
Parameters
file required |
The file to upload (multipart/form-data) |
app_name required |
Name of the application |
version required |
Application version number |
List all Apps
List all non-deleted files in your organization
Response Fields
id |
Unique file identifier |
name |
Original filename |
version |
Application version number |
os |
Operating system: android or ios |
link |
Pre-signed download URL (valid for 5 minutes) |
Get App by ID or Latest
Get a specific app by ID or retrieve the latest uploaded app. Use /v1/apps/latest to get your organization's most recently uploaded app, or /v1/apps/{id} with a specific app ID. Excludes soft-deleted files.
Path Parameters
id required |
The app ID or latest to get the most recently uploaded app |
Response Fields
id |
Unique file identifier |
name |
Original filename |
version |
Application version number |
os |
Operating system: android or ios |
link |
Pre-signed download URL (valid for 5 minutes) |
Delete App
Delete files by ID
Parameters
files required |
Array of file IDs to delete |
Test Cases API
List Available Tests
List all test cases in your organization. Optionally filter by category using the category query parameter.
Query Parameters
category optional |
Category ID to filter test cases. If provided, only test cases in this category will be returned. |
Response Fields
id |
Test case unique identifier |
name |
Test case name |
status |
Test case status: active or inactive |
category |
Category object (if assigned) with id and name |
latest_completed_run |
Most recent completed test run object (if any) with id, link, status, priority, progress, device, executed_by, and app |
Run Individual Tests
Queue test runs
Parameters
jobs required |
Array of job configurations |
jobs[].device required |
Device configuration object |
jobs[].device.name required |
Device name (e.g., "Google Pixel 6") |
jobs[].device.os_version required |
OS version (e.g., "14") |
jobs[].device.platform required |
Device platform (e.g., "android", "ios") |
jobs[].device.orientation optional |
Device orientation (e.g., "portrait", "landscape") |
jobs[].app_file_id required |
ID of the uploaded application file |
jobs[].test_case_id required |
ID of the test case to run |
Run All Tests
Run all test cases against an application. Optionally filter by category.
Parameters
app_file_id required |
ID of the uploaded application file |
device required |
Device configuration object |
device.name required |
Device name (e.g., "Pixel 6") |
device.platform required |
Device platform (e.g., "Android", "iOS") |
device.os_version required |
OS version (e.g., "14.0") |
category_id optional |
Category ID to filter test cases. If provided, only test cases in this category will be run. |
Jobs API
List all Jobs
List all test runs in your organization
Response Fields
id |
Test run unique identifier |
link |
URL to view test run details |
test_case |
Associated test case object with id and name |
app |
Application file object with id and name |
status |
Test run status: queued, running, passed, or failed |
priority |
Test run priority: low, medium, or high |
progress |
Test run progress percentage (0-100) |
executed_by |
User who created the test run (id, name, email) |
device |
Device name where test is running |
Get Job Status
Get status of a specific test run
Path Parameters
test_run_id required |
The ID of the test run |
Response Fields
id |
Test run unique identifier |
test_case |
Associated test case object with id and name |
app |
Application file object with id and name |
status |
Test run status: completed, failed, queued, or running |
priority |
Test run priority level |
progress |
Test run progress (0-100) |
executed_by |
User who created the test run (object with id, name, email) |
device |
Device name |
Categories API
List all Categories
List all categories belonging to your organization
Response Fields
id |
Category unique identifier |
name |
Category name |
description |
Category description |
Devices API
List all Available Devices
List all available devices.
Response Fields
device_type |
Dictionary key representing the device type (e.g., "phone", "tablet") |
device_type[].name |
Device name (e.g., "Google Pixel 7", "iPad Pro") |
device_type[].os_version |
Operating system version (e.g., "13.0", "16.0") |
device_type[].platform |
Device platform (e.g., "Android", "iOS") |
Example Response
{
"phone": [
{
"name": "Pixel 7",
"os_version": "13.0",
"platform": "Android",
"device_type": "phone"
},
{
"name": "iPhone 14 Pro",
"os_version": "16.0",
"platform": "iOS",
"device_type": "phone"
}
],
"tablet": [
{
"name": "iPad Pro",
"os_version": "16.0",
"platform": "iOS",
"device_type": "tablet"
}
]
}
Error codes
QualGent uses conventional HTTP response codes to indicate the success or failure of an API request.
| Code | Description |
|---|---|
200 |
Success |
400 |
Bad Request β The request was unacceptable, often due to missing a required parameter |
401 |
Unauthorized β Invalid or missing API key |
403 |
Forbidden β The API key doesn't have permissions to perform the request |
404 |
Not Found β The requested resource doesn't exist |
413 |
Payload Too Large β Request entity exceeds size limits |
422 |
Unprocessable Entity β The request was well-formed but contains invalid data |
429 |
Too Many Requests β Rate limit exceeded. Wait for the time specified in the Retry-After header before making another request |
500 |
Internal Server Error β Something went wrong on QualGent's end |