Test runs
A test run is one run of a product's Playwright tests against its deployment in an environment, numbered per workspace, on one machine or split into shards across several. Start a shard, upload each test's files, then finish the shard with every test's first attempt; the run finishes with its last shard. A retry runs the failed tests again on the same commit, and a failed test that passes every retry attempt counts as flaky.
The TestRun object
A test run with its shards, its retries and every test with its attempts.
Attributes
idstringRequiredUnique ID of the test run.
numbernumberRequiredThe run's number, counting up from 1 in each workspace, such as 1289.
urlstringRequiredThe run's page on the website.
statestringRequiredrunning until its last shard finishes, then finished, or interrupted when any shard was. One still running after 6 hours becomes interrupted.
runningfinishedinterruptedresultnullable stringRequiredpassed when the run has no error and no failed test, otherwise failed. Flaky tests count as passed. Null while running and for interrupted runs.
passedfailedcountsobjectRequiredHow many tests have each status.
passRatenullable numberRequired(passed + flaky) ÷ (tests − skipped), from 0 to 1. Null while running or when no test counts.
firstAttemptPassRatenullable numberRequiredTests that passed on attempt 1 ÷ (tests − skipped), from 0 to 1. Retries never change it. Null while running or when no test counts.
errornullable stringRequiredWhat failed outside the tests, such as a web server that did not start, or No tests ran. In a run with several shards, each shard's error starts with Shard 2 of 4:. A run with an error is failed and cannot be retried.
productobjectRequiredThe product the run tested.
environmentobjectRequiredThe environment the run tested.
baseUrlstringRequiredThe URL the tests ran against.
deploymentBaseUrlnullable stringRequiredThe deployment's URL when the run started, or null. It differs from baseUrl when clanker run --base-url overrode it.
deploymentVersionnullable stringRequiredThe deployment's version when the run started, or null.
gitobjectRequiredThe commit the run tested.
coveragenullable stringRequiredThe coverage level the run selected, or null when it selected none.
criticalacceptanceregressionexhaustiveshardCountnumberRequiredHow many shards the run is split into, 1 for a run started without one.
finishedShardsnumberRequiredHow many of its shards have finished or been interrupted. The run finishes with its last.
cleanupnullable objectRequiredWhat deleting every shard's scope reported, or null when the deployment had no Fixture API.
publishedByobjectRequiredWho published a run or a retry: a person, an account API key, or one since removed.
startedAtstringRequiredWhen the run started, as an ISO 8601 timestamp.
finishedAtnullable stringRequiredWhen the run finished or was marked interrupted, as an ISO 8601 timestamp, or null while running.
durationMsnumberRequiredHow long the run took, in milliseconds: its longest shard's duration, as reported. 0 while running.
shardsarray of objectsRequiredThe shards that have started, by index.
retriesarray of objectsRequiredThe run's retries, oldest first.
testsarray of objectsRequiredEvery test, failed first, then flaky, passed and skipped, by title.
{ "id": "Tr8KcW3nPx5Q", "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "state": "finished", "result": "passed", "counts": { "total": 2, "passed": 1, "flaky": 1, "failed": 0, "skipped": 0 }, "passRate": 1, "firstAttemptPassRate": 0.5, "error": null, "product": { "slug": "storefront", "name": "Storefront" }, "environment": { "slug": "staging", "name": "Staging", "class": "non-production" }, "baseUrl": "https://staging.acme.dev", "deploymentBaseUrl": "https://staging.acme.dev", "deploymentVersion": "9f2c41e", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "coverage": null, "shardCount": 1, "finishedShards": 1, "cleanup": { "scopes": [ "scp_7h2k9q2m4x1a" ], "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z", "durationMs": 151230, "shards": [ { "index": 1, "state": "finished", "machine": { "hostname": "maya-mbp", "platform": "darwin" }, "testCount": 2, "error": null, "cleanup": { "scope": "scp_7h2k9q2m4x1a", "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "durationMs": 151230, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z" } ], "retries": [ { "number": 1, "state": "finished", "requiredPasses": 2, "testCount": 1, "flakyCount": 1, "baseUrl": "https://staging.acme.dev", "gitSha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "cleanup": { "scope": "scp_q8w3e5r7t9y2", "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 12, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:44:05.000Z", "finishedAt": "2026-10-14T09:44:22.000Z" } ], "tests": [ { "caseId": "checkout-applies-coupon", "title": "checkout › applies a coupon", "file": "checkout.spec.ts", "coverage": "critical", "testKind": "behavior", "priority": null, "status": "flaky", "durationMs": 4105, "errorMessage": "Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")", "files": [ "case.md", "spec.ts" ], "attempts": [ { "number": 1, "shard": 1, "retry": null, "status": "failed", "durationMs": 30412, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": "Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")", "errorStack": "Error: Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")\n at checkout.spec.ts:24:41", "files": [ "video.webm", "trace.zip", "screenshot-1.png" ] }, { "number": 2, "shard": null, "retry": 1, "status": "passed", "durationMs": 4210, "startedAt": "2026-10-14T09:44:07.000Z", "errorMessage": null, "errorStack": null, "files": [] }, { "number": 3, "shard": null, "retry": 1, "status": "passed", "durationMs": 4105, "startedAt": "2026-10-14T09:44:13.000Z", "errorMessage": null, "errorStack": null, "files": [] } ] }, { "caseId": "docs-search-finds-guides", "title": "docs › search finds guides", "file": "docs.spec.ts", "coverage": "acceptance", "testKind": "journey", "priority": null, "status": "passed", "durationMs": 6820, "errorMessage": null, "files": [ "case.md", "spec.ts" ], "attempts": [ { "number": 1, "shard": 1, "retry": null, "status": "passed", "durationMs": 6820, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": null, "errorStack": null, "files": [] } ] } ]}List test runs
GET/Returns the workspace's runs, newest first, 50 per page. Send the response's nextCursor as cursor for the next page.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
Query parameters
productstringOnly runs of this product, by slug.
environmentstringOnly runs in this environment, by slug.
resultstringOnly runs that passed or failed, or flaky for runs with at least one flaky test.
passedfailedflakyqstringMatches part of the branch or the commit subject, or the start of a commit SHA.
commitstringOnly runs of this full commit SHA.
minestringtrue for only the runs you published.
truecursorstringThe nextCursor of the previous page.
Returns
Returns an object with these fields. Status 200.
runsarray of objectsRequiredUp to 50 runs, newest first.
nextCursornullable numberRequiredThe cursor for the next page, or null on the last page.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl https://api.qa.clankership.net/v1/workspaces/acme/test-runs \ -H "Authorization: Bearer $CLANKER_API_KEY"{ "runs": [ { "id": "Tr8KcW3nPx5Q", "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "state": "finished", "result": "passed", "counts": { "total": 2, "passed": 1, "flaky": 1, "failed": 0, "skipped": 0 }, "passRate": 1, "firstAttemptPassRate": 0.5, "error": null, "product": { "slug": "storefront", "name": "Storefront" }, "environment": { "slug": "staging", "name": "Staging", "class": "non-production" }, "baseUrl": "https://staging.acme.dev", "deploymentBaseUrl": "https://staging.acme.dev", "deploymentVersion": "9f2c41e", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "coverage": null, "shardCount": 1, "finishedShards": 1, "cleanup": { "scopes": [ "scp_7h2k9q2m4x1a" ], "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z", "durationMs": 151230 } ], "nextCursor": null}Start a test run
POST/Starts a shard of a running run of a product's deployment in an environment and records the deployment's URL and version. Without shard it starts a new run of one shard. With shard, the first request with its key starts a run of count shards and later ones join it; a shard that already started, another count or commit, another publisher or a run that is no longer running fails the request. When the deployment has a Fixture API, each shard gets a scope of its own and a fixture token to create its test data with. While it runs, its publisher uploads each file as the raw body of PUT /v1/workspaces/{slug}/test-runs/{run}/files/{path}, with Content-Length and at most 100 MB, deletes the scope, then finishes the shard. Viewers cannot publish runs, and an archived product or environment or a missing deployment fails the request.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
Body parameters
productstringRequiredThe product to test, by slug, such as storefront.
environmentstringRequiredThe environment it is deployed to, by slug, such as staging.
baseUrlstringRequiredThe URL the tests run against, usually the deployment's baseUrl.
fixtureUrlnullable stringUse this Fixture API instead of the deployment's, such as a local server on another port. The fixture token is only valid there.
gitobjectRequiredThe commit being tested.
machineobjectRequiredThe computer running the tests.
selectionobjectRequiredWhich tests the run selects.
shardnullable objectThis machine's share of a run split across machines. The first shard with a key starts the run; the others join it. Omit it for a run of one shard.
Returns
Returns an object with these fields. Status 201.
numbernumberRequiredThe run's number in the workspace. Later calls name the run by it.
urlstringRequiredThe run's page on the website.
shardobjectRequiredThe shard this machine started; 1 of 1 without one.
The shard's scope and fixture token, or null when the deployment has no Fixture API.
semaphoresobjectRequiredWhere a shard or retry takes semaphore leases, and the run token it takes them with.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl -X POST https://api.qa.clankership.net/v1/workspaces/acme/test-runs \ -H "Authorization: Bearer $CLANKER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product": "storefront", "environment": "staging", "baseUrl": "https://staging.acme.dev", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "machine": { "hostname": "maya-mbp", "platform": "darwin" }, "selection": { "coverage": null, "folders": [], "caseIds": [] }, "shard": { "index": 2, "count": 4, "key": "github-3905-1" } }'{ "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "shard": { "index": 2, "count": 4 }, "fixture": { "scope": "scp_7h2k9q2m4x1a", "token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImZ4XzEifQ.eyJzY29wZSI6InNjcF83aDJrOXEybTR4MWEifQ.c2lnbmF0dXJl", "url": "https://staging.acme.dev/fixtures", "expiresAt": "2026-10-14T15:40:00.000Z" }, "semaphores": { "url": "https://api.clankertest.com/v1/workspaces/Wk7Qm2RtXa9L/semaphores", "token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImZ4XzEifQ.eyJraW5kIjoicnVuIiwicnVuIjoxMjg5fQ.c2lnbmF0dXJl", "expiresAt": "2026-10-14T15:40:00.000Z" }}Get a test run
GET/Returns one run with its shards, its retries and every test with its attempts. Download a stored file with GET /v1/workspaces/{slug}/test-runs/{run}/files/{path}, which accepts a Range header.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
runintegerRequiredThe run's number in the workspace, such as 1289.
Returns
Returns the TestRun object. Status 200.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl https://api.qa.clankership.net/v1/workspaces/acme/test-runs/{run} \ -H "Authorization: Bearer $CLANKER_API_KEY"{ "id": "Tr8KcW3nPx5Q", "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "state": "finished", "result": "passed", "counts": { "total": 2, "passed": 1, "flaky": 1, "failed": 0, "skipped": 0 }, "passRate": 1, "firstAttemptPassRate": 0.5, "error": null, "product": { "slug": "storefront", "name": "Storefront" }, "environment": { "slug": "staging", "name": "Staging", "class": "non-production" }, "baseUrl": "https://staging.acme.dev", "deploymentBaseUrl": "https://staging.acme.dev", "deploymentVersion": "9f2c41e", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "coverage": null, "shardCount": 1, "finishedShards": 1, "cleanup": { "scopes": [ "scp_7h2k9q2m4x1a" ], "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z", "durationMs": 151230, "shards": [ { "index": 1, "state": "finished", "machine": { "hostname": "maya-mbp", "platform": "darwin" }, "testCount": 2, "error": null, "cleanup": { "scope": "scp_7h2k9q2m4x1a", "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "durationMs": 151230, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z" } ], "retries": [ { "number": 1, "state": "finished", "requiredPasses": 2, "testCount": 1, "flakyCount": 1, "baseUrl": "https://staging.acme.dev", "gitSha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "cleanup": { "scope": "scp_q8w3e5r7t9y2", "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 12, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:44:05.000Z", "finishedAt": "2026-10-14T09:44:22.000Z" } ], "tests": [ { "caseId": "checkout-applies-coupon", "title": "checkout › applies a coupon", "file": "checkout.spec.ts", "coverage": "critical", "testKind": "behavior", "priority": null, "status": "flaky", "durationMs": 4105, "errorMessage": "Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")", "files": [ "case.md", "spec.ts" ], "attempts": [ { "number": 1, "shard": 1, "retry": null, "status": "failed", "durationMs": 30412, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": "Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")", "errorStack": "Error: Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")\n at checkout.spec.ts:24:41", "files": [ "video.webm", "trace.zip", "screenshot-1.png" ] }, { "number": 2, "shard": null, "retry": 1, "status": "passed", "durationMs": 4210, "startedAt": "2026-10-14T09:44:07.000Z", "errorMessage": null, "errorStack": null, "files": [] }, { "number": 3, "shard": null, "retry": 1, "status": "passed", "durationMs": 4105, "startedAt": "2026-10-14T09:44:13.000Z", "errorMessage": null, "errorStack": null, "files": [] } ] }, { "caseId": "docs-search-finds-guides", "title": "docs › search finds guides", "file": "docs.spec.ts", "coverage": "acceptance", "testKind": "journey", "priority": null, "status": "passed", "durationMs": 6820, "errorMessage": null, "files": [ "case.md", "spec.ts" ], "attempts": [ { "number": 1, "shard": 1, "retry": null, "status": "passed", "durationMs": 6820, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": null, "errorStack": null, "files": [] } ] } ]}Finish a shard
POST/Records the shard's tests with their first attempts and closes the shard. The last shard to finish closes the run: it counts every shard's tests and ends the run finished, or interrupted when any shard was. Until then the run stays running and finishedShards counts up. Only the run's publisher can finish a shard, and only while it is running. A finished run with no tests gets the error No tests ran. and fails.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
runintegerRequiredThe run's number in the workspace, such as 1289.
shardintegerRequiredThe shard's index in the run, such as 2; 1 for a run started without a shard.
Body parameters
interruptedbooleanRequiredWhether the shard stopped early, such as on Ctrl-C. Its tests count, and the run ends interrupted with no result.
errornullable stringWhat Playwright reported outside the tests, such as a web server that did not start. It fails the run. Ignored when interrupted is true.
durationMsnumberRequiredHow long the shard took, in milliseconds.
cleanupnullable objectWhat deleting the shard's scope reported. Required for a shard with a scope; a scope with no report counts as incomplete.
testsarray of objectsRequiredEvery test the shard ran, each with a caseId no other shard of the run reported. A run holds up to 5,000 tests.
Returns
Returns an object with these fields. Status 200.
A test run without its retries and tests: what it tested, its state, result and counts.
idstringRequiredUnique ID of the test run.
numbernumberRequiredThe run's number, counting up from 1 in each workspace, such as 1289.
urlstringRequiredThe run's page on the website.
statestringRequiredrunning until its last shard finishes, then finished, or interrupted when any shard was. One still running after 6 hours becomes interrupted.
runningfinishedinterruptedresultnullable stringRequiredpassed when the run has no error and no failed test, otherwise failed. Flaky tests count as passed. Null while running and for interrupted runs.
passedfailedcountsobjectRequiredHow many tests have each status.
passRatenullable numberRequired(passed + flaky) ÷ (tests − skipped), from 0 to 1. Null while running or when no test counts.
firstAttemptPassRatenullable numberRequiredTests that passed on attempt 1 ÷ (tests − skipped), from 0 to 1. Retries never change it. Null while running or when no test counts.
errornullable stringRequiredWhat failed outside the tests, such as a web server that did not start, or No tests ran. In a run with several shards, each shard's error starts with Shard 2 of 4:. A run with an error is failed and cannot be retried.
productobjectRequiredThe product the run tested.
environmentobjectRequiredThe environment the run tested.
baseUrlstringRequiredThe URL the tests ran against.
deploymentBaseUrlnullable stringRequiredThe deployment's URL when the run started, or null. It differs from baseUrl when clanker run --base-url overrode it.
deploymentVersionnullable stringRequiredThe deployment's version when the run started, or null.
gitobjectRequiredThe commit the run tested.
coveragenullable stringRequiredThe coverage level the run selected, or null when it selected none.
criticalacceptanceregressionexhaustiveshardCountnumberRequiredHow many shards the run is split into, 1 for a run started without one.
finishedShardsnumberRequiredHow many of its shards have finished or been interrupted. The run finishes with its last.
cleanupnullable objectRequiredWhat deleting every shard's scope reported, or null when the deployment had no Fixture API.
publishedByobjectRequiredWho published a run or a retry: a person, an account API key, or one since removed.
startedAtstringRequiredWhen the run started, as an ISO 8601 timestamp.
finishedAtnullable stringRequiredWhen the run finished or was marked interrupted, as an ISO 8601 timestamp, or null while running.
durationMsnumberRequiredHow long the run took, in milliseconds: its longest shard's duration, as reported. 0 while running.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl -X POST https://api.qa.clankership.net/v1/workspaces/acme/test-runs/{run}/shards/{shard}/finish \ -H "Authorization: Bearer $CLANKER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interrupted": false, "error": null, "durationMs": 151230, "cleanup": { "status": "complete", "deleted": 214, "remaining": 0, "error": null }, "tests": [ { "caseId": "checkout-applies-coupon", "title": "checkout › applies a coupon", "file": "checkout.spec.ts", "coverage": "critical", "testKind": "behavior", "priority": null, "files": [ "case.md", "spec.ts" ], "attempt": { "status": "failed", "durationMs": 30412, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": "Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")", "errorStack": "Error: Timed out 5000ms waiting for expect(locator).toHaveText(\"$45.00\")\n at checkout.spec.ts:24:41", "files": [ "video.webm", "trace.zip", "screenshot-1.png" ] } }, { "caseId": "docs-search-finds-guides", "title": "docs › search finds guides", "file": "docs.spec.ts", "coverage": "acceptance", "testKind": "journey", "priority": null, "files": [ "case.md", "spec.ts" ], "attempt": { "status": "passed", "durationMs": 6820, "startedAt": "2026-10-14T09:40:12.000Z", "errorMessage": null, "errorStack": null, "files": [] } } ] }'{ "id": "Tr8KcW3nPx5Q", "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "state": "finished", "result": "passed", "counts": { "total": 2, "passed": 1, "flaky": 1, "failed": 0, "skipped": 0 }, "passRate": 1, "firstAttemptPassRate": 0.5, "error": null, "product": { "slug": "storefront", "name": "Storefront" }, "environment": { "slug": "staging", "name": "Staging", "class": "non-production" }, "baseUrl": "https://staging.acme.dev", "deploymentBaseUrl": "https://staging.acme.dev", "deploymentVersion": "9f2c41e", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "coverage": null, "shardCount": 1, "finishedShards": 1, "cleanup": { "scopes": [ "scp_7h2k9q2m4x1a" ], "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z", "durationMs": 151230}Start a retry
POST/Starts a retry of a finished run's failed tests and returns them with the URL to test. The run must have no error, git.sha must match its commit and only one retry runs at a time, or the request fails with Conflict; viewers cannot retry runs. Its publisher uploads the retry's files like the run's, then finishes the retry.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
runintegerRequiredThe run's number in the workspace, such as 1289.
Body parameters
passesnumberRequiredHow many passing attempts a failed test needs to count as flaky, 1 to 5.
baseUrlnullable stringRequiredThe URL to test instead of the run's, or null for the run's.
fixtureUrlnullable stringUse this Fixture API instead of the deployment's, such as a local server on another port. The fixture token is only valid there.
gitobjectRequiredThe commit being tested. sha must be the commit the run tested.
machineobjectRequiredThe computer running the tests.
Returns
Returns an object with these fields. Status 201.
retrynumberRequiredThe retry's number within the run.
requiredPassesnumberRequiredHow many passing attempts a failed test needs to count as flaky.
baseUrlstringRequiredThe URL to test.
testsarray of objectsRequiredThe run's failed tests, to run again.
environmentobjectRequiredThe environment the run tested.
A new scope and fixture token for the retry, or null when the run had no Fixture API.
semaphoresobjectRequiredWhere a shard or retry takes semaphore leases, and the run token it takes them with.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl -X POST https://api.qa.clankership.net/v1/workspaces/acme/test-runs/{run}/retries \ -H "Authorization: Bearer $CLANKER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "passes": 2, "baseUrl": null, "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "machine": { "hostname": "maya-mbp", "platform": "darwin" } }'{ "retry": 1, "requiredPasses": 2, "baseUrl": "https://staging.acme.dev", "tests": [ { "caseId": "checkout-applies-coupon", "title": "checkout › applies a coupon", "file": "checkout.spec.ts", "nextAttempt": 2 } ], "environment": { "slug": "staging", "class": "non-production" }, "fixture": { "scope": "scp_7h2k9q2m4x1a", "token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImZ4XzEifQ.eyJzY29wZSI6InNjcF83aDJrOXEybTR4MWEifQ.c2lnbmF0dXJl", "url": "https://staging.acme.dev/fixtures", "expiresAt": "2026-10-14T15:40:00.000Z" }, "semaphores": { "url": "https://api.clankertest.com/v1/workspaces/Wk7Qm2RtXa9L/semaphores", "token": "eyJhbGciOiJFZERTQSIsImtpZCI6ImZ4XzEifQ.eyJraW5kIjoicnVuIiwicnVuIjoxMjg5fQ.c2lnbmF0dXJl", "expiresAt": "2026-10-14T15:40:00.000Z" }}Finish a retry
POST/Adds the retry's attempts to the run and recounts it. A failed test that ran at least requiredPasses times and passed every time becomes flaky. Only the retry's publisher can finish it, and only while it is running.
Path parameters
slugstringRequiredThe workspace's slug or ID, such as acme.
runintegerRequiredThe run's number in the workspace, such as 1289.
retryintegerRequiredThe retry's number within the run, such as 1.
Body parameters
interruptedbooleanRequiredWhether the retry stopped early. The attempts sent still count.
cleanupnullable objectWhat deleting the retry's scope reported. Required for a retry with a scope.
testsarray of objectsRequiredThe tests this retry ran.
Returns
Returns an object with these fields. Status 200.
A test run without its retries and tests: what it tested, its state, result and counts.
idstringRequiredUnique ID of the test run.
numbernumberRequiredThe run's number, counting up from 1 in each workspace, such as 1289.
urlstringRequiredThe run's page on the website.
statestringRequiredrunning until its last shard finishes, then finished, or interrupted when any shard was. One still running after 6 hours becomes interrupted.
runningfinishedinterruptedresultnullable stringRequiredpassed when the run has no error and no failed test, otherwise failed. Flaky tests count as passed. Null while running and for interrupted runs.
passedfailedcountsobjectRequiredHow many tests have each status.
passRatenullable numberRequired(passed + flaky) ÷ (tests − skipped), from 0 to 1. Null while running or when no test counts.
firstAttemptPassRatenullable numberRequiredTests that passed on attempt 1 ÷ (tests − skipped), from 0 to 1. Retries never change it. Null while running or when no test counts.
errornullable stringRequiredWhat failed outside the tests, such as a web server that did not start, or No tests ran. In a run with several shards, each shard's error starts with Shard 2 of 4:. A run with an error is failed and cannot be retried.
productobjectRequiredThe product the run tested.
environmentobjectRequiredThe environment the run tested.
baseUrlstringRequiredThe URL the tests ran against.
deploymentBaseUrlnullable stringRequiredThe deployment's URL when the run started, or null. It differs from baseUrl when clanker run --base-url overrode it.
deploymentVersionnullable stringRequiredThe deployment's version when the run started, or null.
gitobjectRequiredThe commit the run tested.
coveragenullable stringRequiredThe coverage level the run selected, or null when it selected none.
criticalacceptanceregressionexhaustiveshardCountnumberRequiredHow many shards the run is split into, 1 for a run started without one.
finishedShardsnumberRequiredHow many of its shards have finished or been interrupted. The run finishes with its last.
cleanupnullable objectRequiredWhat deleting every shard's scope reported, or null when the deployment had no Fixture API.
publishedByobjectRequiredWho published a run or a retry: a person, an account API key, or one since removed.
startedAtstringRequiredWhen the run started, as an ISO 8601 timestamp.
finishedAtnullable stringRequiredWhen the run finished or was marked interrupted, as an ISO 8601 timestamp, or null while running.
durationMsnumberRequiredHow long the run took, in milliseconds: its longest shard's duration, as reported. 0 while running.
Errors
- 400
InvalidInputThe request is malformed, or a value is not allowed.- 401
UnauthorizedThe request has no valid API key.- 403
ForbiddenThe key's user or role may not do this.- 404
NotFoundThe workspace or object does not exist, or the caller cannot see it.- 409
ConflictThe change conflicts with current data, such as a slug that is already in use.- 500
InternalClanker Ship failed to handle the request. Retry, or report the response's x-request-id.
curl -X POST https://api.qa.clankership.net/v1/workspaces/acme/test-runs/{run}/retries/{retry}/finish \ -H "Authorization: Bearer $CLANKER_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "interrupted": false, "cleanup": { "status": "complete", "deleted": 12, "remaining": 0, "error": null }, "tests": [ { "caseId": "checkout-applies-coupon", "attempts": [ { "status": "passed", "durationMs": 4210, "startedAt": "2026-10-14T09:44:07.000Z", "errorMessage": null, "errorStack": null, "files": [] }, { "status": "passed", "durationMs": 4105, "startedAt": "2026-10-14T09:44:13.000Z", "errorMessage": null, "errorStack": null, "files": [] } ] } ] }'{ "id": "Tr8KcW3nPx5Q", "number": 1289, "url": "https://clankertest.com/w/acme/test-runs/1289", "state": "finished", "result": "passed", "counts": { "total": 2, "passed": 1, "flaky": 1, "failed": 0, "skipped": 0 }, "passRate": 1, "firstAttemptPassRate": 0.5, "error": null, "product": { "slug": "storefront", "name": "Storefront" }, "environment": { "slug": "staging", "name": "Staging", "class": "non-production" }, "baseUrl": "https://staging.acme.dev", "deploymentBaseUrl": "https://staging.acme.dev", "deploymentVersion": "9f2c41e", "git": { "branch": "web-31-docs-search", "sha": "9f2c41e7b8a3d5c6f1e0b2a4d6c8e0f1a3b5c7d9", "dirty": false, "subject": "Add docs search" }, "coverage": null, "shardCount": 1, "finishedShards": 1, "cleanup": { "scopes": [ "scp_7h2k9q2m4x1a" ], "expiresAt": "2026-10-14T15:40:00.000Z", "status": "complete", "deleted": 214, "remaining": 0 }, "publishedBy": { "kind": "user", "name": "Maya Patel", "email": "maya@acme.dev" }, "startedAt": "2026-10-14T09:40:00.000Z", "finishedAt": "2026-10-14T09:42:31.000Z", "durationMs": 151230}