QualGent API

DOCS

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.

1

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.

↓
2

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.

↓
3

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.

Every call to the QualGent API must include an API key. After you create a QualGent account, you'll receive an API key for authentication.

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.

Response
[
  {
    "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:

  1. Create an account at qualgent.ai/signup
  2. From your Dashboard, navigate to Settings, then to the Developer tab to access API key management
  3. 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.

Keep your API keys secure! Your API keys carry many privileges, so be sure to keep them secure. Don't share your API keys in publicly accessible areas such as GitHub, client-side code, or anywhere else.

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 second
  • X-RateLimit-Remaining - The number of requests remaining in the current window
  • X-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.

Response
{
  "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))
}
Response
{
  "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))
}
Response
{
  "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))
}
Response
{
  "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))
}
Response
{
  "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

POST /v1/apps/upload

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

GET /v1/apps/list

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 /v1/apps/{id}

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

POST /v1/apps/delete

Delete files by ID

Parameters

files required Array of file IDs to delete

Test Cases API

List Available Tests

GET /v1/test-cases/list

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

POST /v1/test-cases/run

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

POST /v1/test-cases/run-all

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

GET /v1/jobs/list

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 /v1/jobs/status/{test_run_id}

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

GET /v1/categories/list

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

GET /v1/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

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