QualityMax Partner API v1

REST API for integration partners. Test automation, security scanning, gap analysis, and artifact downloads.

Authentication

All requests require the X-QualityMax-API-Key header with a valid API token.

# Example request
curl -H "X-QualityMax-API-Key: qm-your-api-token" \
     https://app.qualitymax.io/api/v1/partner/jobs/{job_id}/report

Token formats

FormatTypeHow to get
qm-...API TokenSettings → API Keys in QualityMax dashboard
qm_...GitHub Actions KeyGenerated via CI/CD integration setup

Rate Limits

Endpoint typeLimit
Free endpoints60 requests/minute
LLM-powered endpoints10 requests/minute

Base URL

https://app.qualitymax.io/api/v1/partner

Webhook: AI Test Generation

The primary integration point. Send a webhook with a URL, and QualityMax crawls the page, generates Playwright tests, executes them, and POSTs results back to your callback URL.

POST /api/webhooks/{partner_slug} LLM Trigger AI test generation

Triggers the full pipeline: AI crawl with browser, element discovery, Playwright test generation, execution with screenshots/video, quality scoring, and callback delivery.

Authentication

Requires both headers:

HeaderDescription
X-QualityMax-API-KeyYour API token (qm-... or qm_...)
X-Partner-Signaturesha256=<hmac_hex> -- HMAC-SHA256 of the raw request body using your webhook secret

Request

{
  "url": "https://preview-abc.env.yourapp.com",
  "feature_description": "Test that users can login and see their dashboard with 4 task sections",
  "auth": {
    "type": "user_login",
    "username": "test@example.com",
    "password": "testpass123"
  },
  "callback_url": "https://your-api.com/qualitymax-results",
  "custom_data": {
    "deploy_id": "abc-123",
    "branch": "feat/new-feature",
    "pr_number": 42
  },
  "options": {
    "browser": "chromium",
    "headless": true,
    "skip_cookie_consent": false,
    "quality_threshold": 45,
    "auto_execute": true
  }
}

Parameters

FieldTypeDescription
urlstring requiredTarget URL to crawl and test (max 2048 chars)
feature_descriptionstring requiredWhat to test -- natural language instructions (max 8000 chars)
callback_urlstring requiredURL where results will be POSTed (max 2048 chars)
authobjectLogin credentials for authenticated pages
auth.typestringuser_login (username/password authentication)
auth.usernamestringLogin email or username
auth.passwordstringLogin password
custom_dataobjectAny JSON object -- echoed back in the callback (max 4KB)
options.browserstringchromium (default), firefox, or webkit
options.headlessbooleanRun browser headless (default: true)
options.skip_cookie_consentbooleanSkip cookie consent dismissal (default: false)
options.quality_thresholdintegerMinimum quality score 0-100 (default: 45)
options.auto_executebooleanExecute generated tests automatically (default: true)

Response (immediate)

{
  "accepted": true,
  "job_id": "pwj_abc123def456...",
  "message": "Webhook accepted, processing started"
}

Callback (async, POSTed to your callback_url)

When processing completes (typically 30-120 seconds), QualityMax POSTs results to your callback_url with an X-QualityMax-Signature header for verification.

{
  "event": "test_results",
  "custom_data": { "deploy_id": "abc-123", "branch": "feat/new-feature" },
  "workflow_summary": "AI crawl completed, 1 test generated and executed",
  "results": {
    "status": "passed",
    "total_tests": 1,
    "passed": 1,
    "failed": 0,
    "duration_ms": 54600,
    "report_app_url": "https://app.qualitymax.io/#/projects/137",
    "tests": [
      {
        "test": "Login and Dashboard Verification",
        "status": "passed",
        "duration_ms": 4200,
        "screenshot_url": "https://app.qualitymax.io/static/test-executions/...",
        "video_url": "https://app.qualitymax.io/static/test-executions/...",
        "execution_id": "exec_abc123"
      }
    ],
    "failures": []
  }
}

Verifying Callback Signatures

Compute HMAC-SHA256 of the raw callback body using your webhook secret:

# Python
import hmac, hashlib

def verify(body: bytes, secret: str, signature: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

# Node.js
const crypto = require('crypto');
function verify(body, secret, signature) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret).update(body).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(signature));
}

Full Example (curl)

# Compute HMAC signature
BODY='{"url":"https://preview.app.com","feature_description":"Test login","callback_url":"https://api.you.com/results"}'
SIG=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "your-webhook-secret" | cut -d' ' -f2)

curl -X POST https://app.qualitymax.io/api/webhooks/veilstream \
  -H "Content-Type: application/json" \
  -H "X-QualityMax-API-Key: qm-your-token" \
  -H "X-Partner-Signature: sha256=$SIG" \
  -d "$BODY"

Artifact Downloads

Download screenshots, videos, and logs from test executions. All free, no LLM cost.

GET /artifacts/{execution_id}/screenshot Free 60/min

Returns a 302 redirect to the screenshot image file.

Parameters

NameInTypeDescription
execution_idpathstring requiredTest execution result ID

Example

curl -L -H "X-QualityMax-API-Key: qm-..." \
     -o screenshot.png \
     https://app.qualitymax.io/api/v1/partner/artifacts/{execution_id}/screenshot
GET /artifacts/{execution_id}/video Free 60/min

Returns a 302 redirect to the video recording file (WebM/MP4).

Example

curl -L -H "X-QualityMax-API-Key: qm-..." \
     -o recording.webm \
     https://app.qualitymax.io/api/v1/partner/artifacts/{execution_id}/video
GET /artifacts/{execution_id}/logs Free 60/min

Returns structured execution logs: console output, test output, and errors.

Response

{
  "console_logs": [
    { "message": "Navigation to https://...", "type": "info" }
  ],
  "test_output": "Running 1 test...\n  PASS login-flow.spec.ts",
  "error_message": null
}

Job Reports

GET /jobs/{job_id}/report Free 60/min

Full structured report for a partner webhook job. Includes job metadata, crawl results, generated scripts, execution results, and quality scores.

Response

{
  "job": {
    "id": "pwj_abc123...",
    "status": "callback_sent",
    "url": "https://preview.yourapp.com",
    "tests_generated": 1,
    "duration_seconds": 54.6,
    "quality_score": 96
  },
  "crawl": { "status": "completed", "pages_crawled": 1 },
  "scripts": [{ "id": 123, "name": "login-flow", "framework": "playwright" }],
  "executions": [{ "status": "passed", "duration": 4200 }]
}

Repository Import

POST /repositories/import Free 10/min

Import a GitHub or GitLab repository for scanning and analysis. Creates a project automatically.

Request

{
  "url": "https://github.com/your-org/your-repo",
  "branch": "main",
  "name": "My Project",
  "base_url": "https://app.yoursite.com"
}

Parameters

FieldTypeDescription
urlstring requiredGitHub or GitLab HTTPS URL
branchstringBranch to import (default: main)
namestringProject name (auto-generated from repo if omitted)
base_urlstringApplication URL for E2E testing

Response

{
  "success": true,
  "repository_id": 42,
  "project_id": 15,
  "name": "your-repo",
  "already_exists": false
}

SAST Security Scanning

Static Application Security Testing. No LLM cost -- uses Semgrep and Bandit engines.

POST /sast/scan Free 10/min

Trigger a SAST scan on an imported repository. Returns a job_id for polling.

Request

{
  "repository_id": 42,
  "branch": "main",
  "engines": ["semgrep", "bandit"]
}

Response

{
  "job_id": "scan_abc123",
  "status": "pending",
  "status_url": "/api/v1/partner/sast/scan/scan_abc123"
}
GET /sast/scan/{scan_id} Free 60/min

Get SAST scan results with findings, severity counts, and CWE classifications.

Response

{
  "scan_id": "scan_abc123",
  "status": "completed",
  "verdict": "pass",
  "findings_count": 3,
  "critical_count": 0,
  "high_count": 1,
  "medium_count": 2,
  "findings": [
    {
      "rule_id": "sql-injection",
      "severity": "high",
      "file": "src/db.py",
      "line_start": 42,
      "message": "Possible SQL injection",
      "cwe_id": "CWE-89"
    }
  ]
}

API Test Generation

POST /api-tests/generate LLM 10/min

Parse an OpenAPI/Swagger specification and generate API test cases. Provide either inline spec content or a URL to fetch it.

Request

{
  "openapi_url": "https://api.yourapp.com/openapi.json",
  "project_id": 15,
  "instructions": "Focus on authentication and payment endpoints"
}
FieldTypeDescription
openapi_specstringInline OpenAPI JSON or YAML content
openapi_urlstringURL to fetch spec from
project_idintegerProject to associate tests with
instructionsstringAdditional context for generation

Test Cases

POST /test-cases Free 60/min

Create a test case in a project.

Request

{
  "project_id": 15,
  "title": "Login with valid credentials",
  "description": "Verify user can log in",
  "steps": "1. Go to /login\n2. Enter email\n3. Enter password\n4. Click Submit",
  "expected_results": "User is redirected to dashboard",
  "category": "e2e",
  "priority": 1,
  "tags": ["auth", "critical-path"]
}
POST /test-cases/generate LLM 10/min

AI-generate test cases from a natural language description. Uses BYOLLM keys if configured.

Request

{
  "project_id": 15,
  "description": "Generate tests for user authentication flow including login, logout, password reset, and session expiry",
  "count": 5
}

Gap Analysis

POST /gap-analysis LLM 10/min

Analyze a repository for missing test coverage. Identifies gaps and generates test cases for specified categories. Async -- returns a job_id for polling.

Request

{
  "repository_id": 42,
  "categories": ["e2e", "security", "integration"],
  "max_tests_per_category": 10
}

Response

{
  "job_id": "gap_analysis_abc123",
  "status": "queued",
  "status_url": "/api/v1/partner/gap-analysis/gap_analysis_abc123"
}
GET /gap-analysis/{job_id} Free 60/min

Poll gap analysis job status. Returns results when complete.

Response (completed)

{
  "job_id": "gap_analysis_abc123",
  "status": "completed",
  "progress": 1.0,
  "result": {
    "tests_generated": 15,
    "categories": { "e2e": 5, "security": 5, "integration": 5 }
  }
}

Error Handling

All errors return a JSON object with a detail field.

StatusMeaning
400Bad request -- invalid parameters or missing fields
401Unauthorized -- missing or invalid API key
403Forbidden -- no access to this resource
404Not found -- resource does not exist
429Rate limit exceeded -- wait and retry
503Service unavailable -- try again later

Example error response

{
  "detail": "Repository not found. Import it first via POST /api/v1/partner/repositories/import"
}