Overview
The Thunders CI API lets you queue test executions, poll their status, and retrieve structured reports (JUnit XML or JSON) — all through 3 endpoints under /api/ci/.
API Endpoints
POST https://api.thunders.ai/api/ci/run
GET https://api.thunders.ai/api/ci/run/{runId}
GET https://api.thunders.ai/api/ci/run/{runId}/report
Authentication
The API requires authentication using a Bearer token:
Authorization: Bearer YOUR_THUNDER_TEST_TOKEN
You must obtain a valid API token from the Thunders platform. This token should be stored securely as a secret in your environment or GitHub repository.
Headers
Header | Value | Description |
|
| Authentication token |
|
| Indicates a machine-to-machine API call |
|
| Specifies the request body format |
POST /api/ci/run — Queue Test Runs
Queues test executions for one or more test cases and/or test sets. All runs are grouped under a shared runId. Returns URLs you can use to poll status and fetch the final report.
Request Body
At least one of testCaseIds or testSetIds must be provided. Both can be provided in the same request and in that case both will be run.
{
"projectId": "c8e34ec4-2464-43c7-8db8-1b3a47a22337",
"testCaseIds": [
"a836fadc-377a-46fe-96b0-21f37c626bf9",
"61015c47-612f-47fa-82a6-41d910832c1b"
],
"testSetIds": [
"8eea2fd3-da6d-4434-a310-176be84c5646"
],
"environmentId": "eca24252-e566-40a8-b2b0-707b7efa85d8",
"personaId": "70d0ac52-fe94-4d89-aba9-8ef28dd8c04c",
"maxParallelism": 5,
"maxRetries": 1,
"browserSettings": {
"location": "Paris",
"resolution": "1440x900",
"deviceType": "Desktop"
}
}Parameter | Type | Required | Default | Description |
|
| Yes | - | The unique identifier of the project. |
|
| No | - | List of test case IDs to run. |
|
| No | - | List of test set IDs. Each set expands to its test cases. |
|
| Yes | - | The environment to use. |
|
| Yes | - | The persona for test execution. |
|
| No | 4 | Max parallel tests. |
|
| No | 0 | Retry attempts for failed tests (0-3). |
|
| No | - | Test asset refs for batch execution. |
|
| No |
| Geo cluster: Paris, London, Amsterdam, SanFrancisco, NewYork. |
|
| No |
| Chromium, Chrome, Firefox, or Safari. |
|
| No |
| Desktop, Mobile, or Tablet. |
|
| No |
| Viewport resolution (e.g., "1440x900"). |
|
| No |
| Enable/disable JavaScript. |
|
| No |
| Highlight target elements. |
|
| No |
| Ignore HTTPS errors. |
|
| No |
| Dark theme mode. |
|
| No |
| Bot detection avoidance. |
|
| No | - | Browser locale (e.g., "en-US"). |
|
| No | - | Proxy server configuration. |
|
| No | - | Custom HTTP headers. |
Response — 202 Accepted
{
"runId": "01966a3e-...",
"statusUrl": "/api/ci/run/01966a3e-...",
"reportUrl": "/api/ci/run/01966a3e-.../report"
}Field | Type | Description |
|
| Unique identifier for the CI run. |
|
| Relative URL to poll the run's status. |
|
| Relative URL to fetch the final report. |
GET /api/ci/run/{runId} — Poll Run Status
Returns aggregated status of all test runs. Poll until status is terminal (passed, failed, stopped, cancelled).
Response — 200 OK
{
"runId": "01966a3e-...",
"status": "running",
"testCount": 5,
"passedCount": 2,
"failedCount": 0,
"runningCount": 2,
"queuedCount": 1,
"startedAt": "2026-03-20T14:30:00Z",
"endedAt": null,
"durationMs": null
}Field | Type | Description |
|
| The CI run identifier. |
|
|
|
|
| Total test runs. |
|
| Tests passed. |
|
| Tests failed. |
|
| Tests running. |
|
| Tests queued. |
|
| When earliest test started. |
|
| When last test ended. |
|
| Wall-clock duration in ms. |
GET /api/ci/run/{runId}/report — Fetch Report
Returns the test execution report. Format depends on the Accept header:
Default (or
application/xml): JUnit XML — compatible with Jenkins, GitHub Actions (dorny/test-reporter), GitLab, etc.Accept: application/json: JSON report with structured data.
JSON Response Example
{
"tests": 3,
"failures": 1,
"skipped": 0,
"time": 45.2,
"testSuites": [
{
"name": "My Test Set",
"tests": 2,
"failures": 1,
"skipped": 0,
"time": 30.1,
"timestamp": "2026-03-20T14:30:00Z",
"testCases": [
{
"name": "Login flow",
"className": "My Test Set",
"time": 15.0,
"status": "passed",
"failure": null
},
{
"name": "Checkout flow",
"className": "My Test Set",
"time": 15.1,
"status": "failed",
"failure": {
"message": "Element not found",
"type": "AssertionFailure",
"body": "Step 3: Click Add to cart — element not found."
}
}
]
}
]
}Field | Type | Description |
|
| Total test cases across all suites. |
|
| Total failed test cases. |
|
| Total skipped test cases. |
|
| Total execution time in seconds. |
|
| List of test suite reports. |
|
|
|
Error Handling
The API uses standard HTTP status codes:
202 Accepted:POST /api/ci/run— tests were successfully queued200 OK:GETendpoints — request was successful400 Bad Request: Malformed request or missing required parameters401 Unauthorized: Authentication failed or token is invalid403 Forbidden: Insufficient permissions404 Not Found: No test runs found for the givenrunId500 Internal Server Error: Unexpected server error
Using with GitHub Actions
Integrate the Thunders CI API with GitHub Actions to automate test execution in your CI/CD pipeline.
Example GitHub Action Workflow
name: Run Thunders Tests
on:
workflow_dispatch:
permissions:
contents: read
checks: write
jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Queue Thunders CI Run
id: trigger
run: |
set -euo pipefail
TMPFILE=$(mktemp)
curl --no-buffer -s -X POST https://api.thunders.ai/api/ci/run \
-H "Authorization: Bearer $\{{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M" \
-H "Content-Type: application/json" \
-d '{
"projectId": "YOUR_PROJECT_ID",
"testSetIds": ["YOUR_TEST_SET_ID"],
"environmentId": "YOUR_ENVIRONMENT_ID",
"personaId": "YOUR_PERSONA_ID",
"maxParallelism": 5,
"maxRetries": 1,
"browserSettings": {
"location": "Paris",
"resolution": "1920x1080",
"deviceType": "Desktop"
}
}' \
-w '\n%{http_code}' 2>&1 | tee "$TMPFILE"
HTTP_CODE=$(tail -1 "$TMPFILE")
BODY=$(head -n -1 "$TMPFILE")
if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
echo "Failed (HTTP $HTTP_CODE): $BODY"; exit 1
fi
echo "status_url=$(echo $BODY | jq -r '.statusUrl')" >> $GITHUB_OUTPUT
echo "report_url=$(echo $BODY | jq -r '.reportUrl')" >> $GITHUB_OUTPUT
- name: Wait for completion
id: run-tests
run: |
set -euo pipefail
MAX_WAIT=5400; INTERVAL=30; ELAPSED=0
while [ $ELAPSED -lt $MAX_WAIT ]; do
RESP=$(curl -sS --fail-with-body \
"https://api.thunders.ai$\{{ steps.trigger.outputs.status_url }}" \
-H "Authorization: Bearer $\{{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M")
STATUS=$(echo "$RESP" | jq -r '.status')
echo "[$ELAPSED s] status=$STATUS"
case "$STATUS" in
passed|failed|stopped|cancelled)
echo "final_status=$STATUS" >> $GITHUB_OUTPUT; break;;
esac
sleep $INTERVAL; ELAPSED=$((ELAPSED+INTERVAL))
done
[ $ELAPSED -ge $MAX_WAIT ] && echo "Timed out" && exit 1
- name: Download JUnit report
if: always() && steps.run-tests.conclusion == 'success'
run: |
curl -sS --fail-with-body \
"https://api.thunders.ai$\{{ steps.trigger.outputs.report_url }}" \
-H "Authorization: Bearer $\{{ secrets.THUNDER_TEST_TOKEN }}" \
-H "X-MS-API-ROLE: M2M" \
-o test-results.xml
- name: Publish test results
uses: dorny/test-reporter@v1
if: always() && steps.run-tests.conclusion == 'success'
continue-on-error: true
with:
name: Thunders CI Results
path: test-results.xml
reporter: java-junit
- name: Fail if tests did not pass
if: steps.run-tests.outputs.final_status != 'passed'
run: exit 1
Setting Up GitHub Secrets
Go to your GitHub repository
Navigate to Settings > Secrets and variables > Actions
Click "New repository secret"
Name:
THUNDER_TEST_TOKENValue: Your Thunders API token
Click "Add secret"
Customizing the Workflow
You can customize the workflow by:
Changing the trigger events (e.g., on push, on schedule)
Modifying test parameters (
projectId,testCaseIds,testSetIds,environmentId,personaId)Adjusting the polling interval (
INTERVAL) and timeout (MAX_WAIT)Adding additional steps to process test results
Test Asset Batch
The Test Asset Batch feature allows you to run the same test case multiple times with different data variations by uploading CSV files as test assets. Each row in the CSV becomes a separate test run, enabling data-driven testing at scale.
How It Works
Upload CSV files as test assets to your project (via the Thunders web app or API)
Reference the CSV files in your API request using the
testAssetReferencesparameterEach CSV row generates a separate test run with its own variable overrides and browser settings
All runs from the same batch are grouped under a shared
batchId
The testAssetReferences Parameter
Add the testAssetReferences field to your request body:
{
"projectId": "your-project-id",
"testCaseIds": ["your-test-case-id"],
"environmentId": "your-environment-id",
"personaId": "your-persona-id",
"testAssetReferences": ["FILE_MYDATA_CSV", "FILE_USERS_CSV"]
}Reference format: When you upload a file named mydata.csv, its reference becomes FILE_MYDATA_CSV. The naming convention is FILE__ where the filename is uppercased, and special characters are replaced with underscores.
References can also be wrapped in brackets: [FILE_MYDATA_CSV].
To know more about the batching feature check out this documentation
Combining Multiple CSV Files
You can pass multiple CSV references in the testAssetReferences array. Rows from all files are merged sequentially into a single batch with continuous indexing.
"testAssetReferences": ["FILE_BROWSERS_CSV", "FILE_MOBILE_CSV"]
If browsers.csv has 3 rows and mobile.csv has 2 rows, the batch will contain 5 total runs indexed 1 through 5.

