For AI Agents & Developers
API reference, authentication, webhooks, MCP integration, and the auto-fix loop
Quick Start
Get your API key and submit your first test cycle in under 5 minutes.
1. Get an API Key
Sign in at clawqa.ai with GitHub, then go to Settings → API Keys and generate a key. Keys are prefixed with cqa_.
2. Submit a Test Cycle
curl -X POST https://clawqa.ai/api/v1/test-cycles \
-H "Authorization: Bearer cqa_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"projectId": 1,
"title": "Login flow regression test",
"targetUrl": "https://staging.myapp.com",
"steps": [
{
"title": "Login with valid credentials",
"description": "Navigate to /login, enter valid email and password, click Submit",
"expectedResult": "User is redirected to dashboard with welcome message"
},
{
"title": "Login with invalid password",
"description": "Navigate to /login, enter valid email but wrong password, click Submit",
"expectedResult": "Error message shown: Invalid credentials. Form is not cleared."
}
]
}'
import requests
response = requests.post(
"https://clawqa.ai/api/v1/test-cycles",
headers={"Authorization": "Bearer cqa_your_api_key"},
json={
"projectId": 1,
"title": "Login flow regression test",
"targetUrl": "https://staging.myapp.com",
"steps": [
{
"title": "Login with valid credentials",
"description": "Navigate to /login, enter valid email and password, click Submit",
"expectedResult": "User is redirected to dashboard with welcome message"
}
]
}
)
print(response.json()) # {"id": 42, "status": "submitted"}
const response = await fetch("https://clawqa.ai/api/v1/test-cycles", {
method: "POST",
headers: {
"Authorization": "Bearer cqa_your_api_key",
"Content-Type": "application/json"
},
body: JSON.stringify({
projectId: 1,
title: "Login flow regression test",
targetUrl: "https://staging.myapp.com",
steps: [
{
title: "Login with valid credentials",
description: "Navigate to /login, enter valid email and password, click Submit",
expectedResult: "User is redirected to dashboard with welcome message"
}
]
})
});
const cycle = await response.json();
console.log(cycle); // { id: 42, status: "submitted" }
Authentication
All authenticated endpoints require a Bearer token in the Authorization header:
Authorization: Bearer cqa_your_api_key
API keys:
- Are prefixed with
cqa_followed by 48 random characters - Are scoped to the user who created them
- Can be revoked at any time from Settings → API Keys
- Should be stored securely and never committed to version control
Unauthenticated requests to protected endpoints return 401 Unauthorized.
Creating Test Cycles
A test cycle represents a batch of test steps to be executed by human testers. Send a POST request to /api/v1/test-cycles:
POST /api/v1/test-cycles
Content-Type: application/json
Authorization: Bearer cqa_...
{
"projectId": 1,
"title": "Checkout flow — v2.3.1",
"targetUrl": "https://staging.myapp.com",
"steps": [
{
"title": "Add item to cart",
"description": "Search for 'wireless headphones', click first result, click 'Add to Cart'",
"expectedResult": "Cart badge shows 1 item. Toast notification confirms addition."
},
{
"title": "Complete checkout",
"description": "Click cart icon, click 'Checkout', fill in test payment details, click 'Place Order'",
"expectedResult": "Order confirmation page shown with order number and estimated delivery date."
}
]
}
Response:
{
"id": 42,
"projectId": 1,
"title": "Checkout flow — v2.3.1",
"status": "submitted",
"stepsCount": 2,
"createdAt": "2026-02-20T01:30:00Z"
}
The cycle is immediately routed to testers. You'll receive webhook events as results come in.
Receiving Results
Register a Webhook
POST /api/v1/webhooks
Authorization: Bearer cqa_...
{
"url": "https://your-agent.example.com/clawqa-webhook",
"events": ["bug_report.created", "bug_report.verified", "bug_report.fix_failed", "cycle.completed"],
"secret": "whsec_your_signing_secret"
}
Webhook Payload: bug_report.created
{
"event": "bug_report.created",
"timestamp": "2026-02-20T03:15:00Z",
"data": {
"id": 101,
"cycleId": 42,
"title": "Checkout button unresponsive on iOS Safari",
"severity": "critical",
"description": "Tapping 'Place Order' does nothing on iPhone 15 Pro, iOS 17.3, Safari.",
"stepsToReproduce": "1. Open staging URL on iPhone\n2. Add item to cart\n3. Proceed to checkout\n4. Fill payment details\n5. Tap 'Place Order'\n6. Nothing happens",
"screenshotUrl": "https://clawqa.ai/uploads/bug-101-screenshot.png",
"device": "iPhone 15 Pro",
"os": "iOS 17.3",
"browser": "Safari 17.3"
}
}
Webhook Payload: bug_report.verified
{
"event": "bug_report.verified",
"timestamp": "2026-02-20T04:30:00Z",
"data": {
"id": 101,
"cycleId": 42,
"fixAttemptId": 5,
"status": "verified",
"verifiedBy": "human_tester",
"notes": "Fix confirmed — Place Order button now works correctly on iOS Safari."
}
}
The Auto-Fix Loop
When your agent receives a bug_report.created webhook, it enters the fix loop:
Submit a fix:
POST /api/v1/bugs/101/fix
Authorization: Bearer cqa_...
{
"commitUrl": "https://github.com/myorg/myapp/commit/abc123",
"deployUrl": "https://staging.myapp.com",
"notes": "Fixed iOS Safari touch event handler — was using click instead of pointerdown"
}
ClawQA will automatically request re-testing from crowd testers. If the fix passes, you receive a bug_report.verified webhook. If it fails, you receive bug_report.fix_failed with tester notes, and the loop continues.
MCP Integration
ClawQA exposes an MCP (Model Context Protocol) server, allowing AI agents to interact with the platform using the standard for AI tool integration.
MCP Server URL
https://clawqa.ai/api/mcp
Available Tools
── Project Setup ──
clawqa.create_project(name, targetUrl, repoUrl) → {project, generatedPlans[]} (auto-generates plans!)
── Core ──
clawqa.list_projects() → [{id, name, slug, url, plans, cycles}]
clawqa.list_plans(projectId?) → [{id, title, steps, version}]
clawqa.list_cycles(projectId?) → [{id, title, status, plan}]
clawqa.get_bugs(cycleId?, severity?) → [{id, title, severity, steps}]
clawqa.get_analytics(projectId?) → {totalCycles, totalBugs, ...}
── Plan Lifecycle ──
clawqa.generate_plans(projectId, focus?) → {plans[], count}
clawqa.classify_steps(planId, classifications[]) → {classificationsCached}
clawqa.enrich_plan_actions(planId, steps[]) → {stepsEnriched}
clawqa.get_page_context(url) → {forms, buttons, links, headings}
clawqa.execute_plan(planId) → {testCycle, testRuns[]}
── Execution ──
clawqa.run_all_plans(projectId) → {results[]}
clawqa.rerun(cycleId, runId) → {rerun}
── Bug Fix ──
clawqa.submit_fix(bugId, commitUrl?, notes?) → {fixAttempt}
clawqa.escalate(cycleId, reason?) → {externalCycleId}
MCP gives agents a structured way to discover and call ClawQA tools without needing to know the REST API details.
Enriching Test Plans (Recommended Workflow)
ClawQA generates test plans with deterministic action matching. For higher-quality Playwright tests, agents should enrich plans with structured actions and step classifications. Here's the recommended workflow:
1. clawqa.create_project(name, targetUrl, repoUrl) → Project created + plans auto-generated!
2. clawqa.enrich_plan_actions(planId, steps) → Improve test accuracy with structured actions
3. clawqa.execute_plan(planId) → Start testing
── Optional enrichment (for higher precision) ──
clawqa.get_page_context(url) → Discover page elements (forms, buttons, links)
clawqa.classify_steps(planId, classifications) → Tell ClawQA which steps are automated vs manual
Steps 2-3 of the enrichment phase are optional. Without them, ClawQA uses deterministic rules (less precise but functional). With them, Playwright tests are significantly more accurate.
classify_steps — Step Classification
Tell ClawQA which steps should be automated (Playwright) vs manual (human testers). Classifications are cached and used during step partitioning when execute_plan is called.
// MCP: clawqa.classify_steps
// REST: POST /api/v1/test-plans/{planId}/classify-steps
{
"planId": "plan_abc123",
"classifications": [
{ "stepIndex": 0, "type": "automated", "reason": "Simple navigation + form submit" },
{ "stepIndex": 1, "type": "automated", "reason": "Page element verification" },
{ "stepIndex": 2, "type": "manual", "reason": "Visual layout check requires human judgment" },
{ "stepIndex": 3, "type": "manual", "reason": "Email verification — external system" }
]
}
enrich_plan_actions — Structured Actions
Provide structured TestAction[] for specific steps. Steps with actions produce precise Playwright code with real locators and assertions.
// MCP: clawqa.enrich_plan_actions
// REST: POST /api/v1/test-plans/{planId}/enrich-actions
{
"planId": "plan_abc123",
"steps": [
{
"stepIndex": 0,
"actions": [
{
"type": "navigate",
"url": "https://myapp.com/login",
"description": "Open login page"
},
{
"type": "fill",
"target": { "strategy": "label", "value": "Email" },
"value": "test@example.com",
"description": "Fill email field"
},
{
"type": "fill",
"target": { "strategy": "label", "value": "Password" },
"value": "testpass123",
"description": "Fill password field"
},
{
"type": "click",
"target": { "strategy": "role", "value": "Sign in" },
"description": "Click sign in button"
}
]
}
]
}
TestAction Reference
Action types: navigate, click, fill, select, check, expect_visible, expect_text, expect_url, expect_count, screenshot, wait
Locator strategies: role, text, label, placeholder, css, testid
Validation rules: 1-6 actions per step. fill requires value. navigate requires url. expect_text requires expectedText. expect_url requires expectedUrl. Click/fill/expect actions require target.
get_page_context — Page Discovery
Crawl a page to discover interactive elements. Use this data to generate accurate actions for enrich_plan_actions.
// MCP: clawqa.get_page_context({ url: "https://myapp.com/login" })
// REST: GET /api/v1/page-context?url=https://myapp.com/login
// Returns:
{
"url": "https://myapp.com/login",
"title": "Login — MyApp",
"hasAuth": true,
"forms": [{
"method": "POST",
"submitButton": "Sign in",
"fields": [
{ "label": "Email", "type": "email", "name": "email", "required": true },
{ "label": "Password", "type": "password", "name": "password", "required": true }
]
}],
"buttons": [{ "text": "Sign in with GitHub", "role": "button" }],
"headings": [{ "level": 1, "text": "Welcome back" }],
"links": [{ "text": "Forgot password?", "href": "/reset-password" }]
}
OpenClaw Skill Example
Here's a complete example of an OpenClaw skill that integrates with ClawQA:
// clawqa-testing-skill.js — OpenClaw Skill
const CLAWQA_API = "https://clawqa.ai/api/v1";
const API_KEY = process.env.CLAWQA_API_KEY;
// Create a test cycle from a PR
async function createTestCycleFromPR(projectId, prData) {
const steps = analyzePRForTestSteps(prData);
const response = await fetch(`${CLAWQA_API}/test-cycles`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
projectId,
title: `PR #${prData.number}: ${prData.title}`,
targetUrl: prData.deployUrl,
steps
})
});
return response.json();
}
// Analyze PR diff to generate test steps
function analyzePRForTestSteps(prData) {
return prData.changedFiles.map(file => ({
title: `Verify changes in ${file.path}`,
description: `Test the functionality affected by changes to ${file.path}. ` +
`${file.additions} lines added, ${file.deletions} lines removed.`,
expectedResult: "Feature works correctly with no regressions."
}));
}
// Handle incoming bug webhook
async function handleBugWebhook(event) {
if (event.event !== "bug_report.created") return;
const bug = event.data;
console.log(`Bug received: ${bug.title} (${bug.severity})`);
const fix = await analyzeBugAndFix(bug);
if (fix) {
await fetch(`${CLAWQA_API}/bugs/${bug.id}/fix`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
commitUrl: fix.commitUrl,
deployUrl: fix.deployUrl,
notes: fix.description
})
});
console.log(`Fix submitted for bug ${bug.id}`);
}
}
module.exports = { createTestCycleFromPR, handleBugWebhook };