curl --request POST \
--url https://app.testorim.com/api/runs/{id}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.testorim.com/api/runs/{id}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.testorim.com/api/runs/{id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.testorim.com/api/runs/{id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.testorim.com/api/runs/{id}/cancel"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.testorim.com/api/runs/{id}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.testorim.com/api/runs/{id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"cancelled": 1,
"reapedHandlers": 1
}{
"error": "Invalid or revoked API key"
}{
"error": "Viewers have read-only access to this workspace",
"code": "read_only_role"
}{
"error": "Run not found"
}{
"error": "<string>",
"retryAfter": 123
}Cancel a single run
Marks the run cancelled if it is still running or pending, then
fires the in-memory cancel handler so the orchestrator tears its
Playwright browser down within seconds instead of waiting for the
orphan killer.
Idempotent in effect: cancelling an already-terminal run succeeds
with cancelled: 0. The endpoint takes no request body.
curl --request POST \
--url https://app.testorim.com/api/runs/{id}/cancel \
--header 'Authorization: Bearer <token>'import requests
url = "https://app.testorim.com/api/runs/{id}/cancel"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://app.testorim.com/api/runs/{id}/cancel', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.testorim.com/api/runs/{id}/cancel",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://app.testorim.com/api/runs/{id}/cancel"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.testorim.com/api/runs/{id}/cancel")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.testorim.com/api/runs/{id}/cancel")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"cancelled": 1,
"reapedHandlers": 1
}{
"error": "Invalid or revoked API key"
}{
"error": "Viewers have read-only access to this workspace",
"code": "read_only_role"
}{
"error": "Run not found"
}{
"error": "<string>",
"retryAfter": 123
}Authorizations
Send Authorization: Bearer tst_live_….
Format (services/api-keys.ts): the literal prefix tst_live_
followed by 24 random bytes rendered as 32 base64url characters.
Only the SHA-256 hash is stored server-side. The shape check that
routes a token down the API-key path rather than the Clerk JWT path
requires the tst_live_ prefix and a total length of at least 25
characters.
Keys are minted in the dashboard at /settings/team. The same header
also accepts a Clerk session JWT, which is how the web app
authenticates, but the JWT path is out of scope for this document.
Path Parameters
Run id (UUID). Returned as runId by POST /api/runs/trigger. Not UUID-validated by the handler; a malformed value simply finds nothing and 404s.
Response
Cancellation processed. cancelled counts DB rows flipped to cancelled (0 or 1 here); reapedHandlers counts live orchestrators actually torn down, which is lower when the process already exited.
Number of DB rows flipped from pending/running to cancelled. Zero when the run(s) had already finished.
x >= 0Number of live orchestrators whose in-memory cancel handler actually fired and tore a browser down. Can be lower than cancelled when a process already exited.
x >= 0
