Worked workflows
Real jobs take more than one call. Each workflow below chains several requests, passing an id or an ETag from one response into the next, and comes as a bash script you can copy and run. They need curl, jq, and your token in BESTEST_TOKEN.
The read-only ones are safe to run anywhere. The ones that write create real records in your Space, so try them in a sandbox Space first. For job-shaped recipes, such as pushing CI results or gating a release, see the recipes.
Find test cases by the results of their executions (relation quantifiers)
The three relation quantifiers from FilterGrammar.quantifierSyntax, on the testCaseExecutions relation: ANY (at least one failed), ALL (every one still unexecuted), NONE (no passed execution). Read-only.
Steps
- 1
anyFailed - 2
allUnexecuted - 3
nonePassed
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
# 1. anyFailed
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'filter=testCaseExecutions ANY (result = "FAILED")' \
--data-urlencode 'page[size]=10' \
| jq .
# 2. allUnexecuted
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'filter=testCaseExecutions ALL (result = "NOT_EXECUTED")' \
--data-urlencode 'page[size]=10' \
| jq .
# 3. nonePassed
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'filter=testCaseExecutions NONE (result = "PASSED")' \
--data-urlencode 'page[size]=10' \
| jq .Count executions per result across the project
The aggregate route with aggregations[count] and one groupBy column returns the grouped shape - one group per distinct result. Read-only.
Steps
- 1
byResult
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
# 1. byResult
curl -sSG "$BESTEST_BASE/api/v1/test_case_executions/aggregate" \
"${AUTH[@]}" \
--data-urlencode 'aggregations[count]=true' \
--data-urlencode 'aggregations[groupBy][]=result' \
| jq .Walk a collection with cursor pagination
An empty page[after]= bootstraps cursor mode; each response's links.next then carries the opaque cursor for the following page. Take page[after] verbatim from links.next - never construct or decode it. Read-only.
Inputs
cursorstring - The page[after] value of the previous response's links.next
Steps
- 1
firstPageHands on next. - 2
nextPage
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
# 1. firstPage
RESPONSE=$(curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'page[size]=2' \
--data-urlencode 'page[after]='
)
echo "$RESPONSE" | jq .
NEXT=$(echo "$RESPONSE" | jq -r '.links.next')
CURSOR=$(echo "$NEXT" | sed -n 's/.*page\[after\]=\([^&]*\).*/\1/p')
# 2. nextPage
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'page[size]=2' \
--data-urlencode "page[after]=$CURSOR" \
| jq .Read one test case, then its steps through the related and linkage routes
Nested to-many retrieval: pick a case from the collection, read it, read its steps in sequence order narrowed to three fields, and read the id-only linkage when only membership is needed (a fraction of the bytes). Read-only.
Steps
- 1
pickHands on caseId. - 2
read - 3
- 4
idsOnly- The same related route narrowed to identifiers only - the id-list form (there is no separate linkage route).
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
# 1. pick
RESPONSE=$(curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'page[size]=1'
)
echo "$RESPONSE" | jq .
CASE_ID=$(echo "$RESPONSE" | jq -r '.data[0].id')
# 2. read
curl -sS "$BESTEST_BASE/api/v1/test_cases/$CASE_ID" \
"${AUTH[@]}" \
| jq .
# 3. steps
curl -sSG "$BESTEST_BASE/api/v1/test_cases/$CASE_ID/testSteps" \
"${AUTH[@]}" \
--data-urlencode 'sort=sequenceNumber' \
--data-urlencode 'fields[test_steps]=sequenceNumber,action,expectedResult' \
| jq .
# 4. idsOnly - The same related route narrowed to identifiers only - the id-list form (there is no separate linkage route).
curl -sSG "$BESTEST_BASE/api/v1/test_cases/$CASE_ID/testSteps" \
"${AUTH[@]}" \
--data-urlencode 'fields[test_steps]=id' \
| jq .Find yourself with currentUser(), then search your test cases with sort and sparse fieldsets
currentUser() resolves to the caller's own users.id, so it matches users.id and every uuid column that references a user. Resolve your own user row first, thread its id into the test-case filter (an expression embedded in a string takes the {$...} form), sort with a "-" prefix for descending, and narrow the attributes with fields[test_cases]. Read-only.
Steps
- 1
meHands on userId. - 2
search
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
# 1. me
RESPONSE=$(curl -sSG "$BESTEST_BASE/api/v1/users" \
"${AUTH[@]}" \
--data-urlencode 'filter=id = currentUser()' \
--data-urlencode 'fields[users]=id'
)
echo "$RESPONSE" | jq .
USER_ID=$(echo "$RESPONSE" | jq -r '.data[0].id')
# 2. search
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode "filter=ownerId = \"$USER_ID\" OR status = \"ACTIVE\"" \
--data-urlencode 'sort=-updatedAt' \
--data-urlencode 'fields[test_cases]=name,status,updatedAt' \
--data-urlencode 'page[size]=10' \
| jq .Create a test case, then its steps, then read them back
Create-with-children over JSON:API: the parent first, each child carrying the parent's id from the create response, then the related route to confirm.
Inputs
namestring - A unique test case name within the projectprojectIduuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.
Steps
- 1
createCaseHands on caseId. - 2
firstStep - 3
secondStep - 4
readBack
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
NAME="API walkthrough $(date +%s)"
PROJECT_ID=$(curl -sS "$BESTEST_BASE/api/v1/projects" "${AUTH[@]}" | jq -r '.data[0].id')
# 1. createCase
RESPONSE=$(curl -sS -X POST "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"name": "$NAME",
"priority": "HIGH",
"projectId": "$PROJECT_ID",
"rank": 1
},
"type": "test_cases"
}
}
JSON
)
echo "$RESPONSE" | jq .
CASE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
# 2. firstStep
curl -sS -X POST "$BESTEST_BASE/api/v1/test_steps" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"action": "Open the login page",
"expectedResult": "The login form is shown",
"projectId": "$PROJECT_ID",
"sequenceNumber": 1,
"testCaseId": "$CASE_ID"
},
"type": "test_steps"
}
}
JSON
# 3. secondStep
curl -sS -X POST "$BESTEST_BASE/api/v1/test_steps" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"action": "Submit valid credentials",
"expectedResult": "The dashboard is shown",
"projectId": "$PROJECT_ID",
"sequenceNumber": 2,
"testCaseId": "$CASE_ID"
},
"type": "test_steps"
}
}
JSON
# 4. readBack
curl -sSG "$BESTEST_BASE/api/v1/test_cases/$CASE_ID/testSteps" \
"${AUTH[@]}" \
--data-urlencode 'sort=sequenceNumber' \
| jq .Create a cycle, add an execution for a case, record its result, review the cycle
A run is a test cycle holding executions. Create the cycle Ready to Execute (status ACTIVE; a DRAFT cycle records no result) and a fresh case, add the execution (the server stamps the case's current version and the run's times; a client sends neither), record the result - an update needs the resource's ETag back as If-Match (optimistic locking) - then read the cycle's executions through the related route.
Inputs
namestring - A unique name for the cycle and the caseprojectIduuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.
Steps
- 1
createCycleHands on cycleId. - 2
createCaseHands on caseId. - 3
addExecutionHands on executionId. - 4
readExecutionHands on etag. - 5
recordResultSends If-Match. - 6
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
NAME="API walkthrough $(date +%s)"
PROJECT_ID=$(curl -sS "$BESTEST_BASE/api/v1/projects" "${AUTH[@]}" | jq -r '.data[0].id')
# 1. createCycle
RESPONSE=$(curl -sS -X POST "$BESTEST_BASE/api/v1/test_cycles" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"name": "$NAME",
"projectId": "$PROJECT_ID",
"rank": 1,
"status": "ACTIVE"
},
"type": "test_cycles"
}
}
JSON
)
echo "$RESPONSE" | jq .
CYCLE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
# 2. createCase
RESPONSE=$(curl -sS -X POST "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"name": "$NAME",
"projectId": "$PROJECT_ID",
"rank": 1
},
"type": "test_cases"
}
}
JSON
)
echo "$RESPONSE" | jq .
CASE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
# 3. addExecution
RESPONSE=$(curl -sS -X POST "$BESTEST_BASE/api/v1/test_case_executions" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"projectId": "$PROJECT_ID",
"testCaseId": "$CASE_ID",
"testCycleId": "$CYCLE_ID"
},
"type": "test_case_executions"
}
}
JSON
)
echo "$RESPONSE" | jq .
EXECUTION_ID=$(echo "$RESPONSE" | jq -r '.data.id')
# 4. readExecution
curl -sS "$BESTEST_BASE/api/v1/test_case_executions/$EXECUTION_ID" \
"${AUTH[@]}" \
-D headers.txt \
| jq .
ETAG=$(grep -i '^ETag:' headers.txt | cut -d' ' -f2- | tr -d '\r')
# 5. recordResult
curl -sS -X PATCH "$BESTEST_BASE/api/v1/test_case_executions/$EXECUTION_ID" \
"${AUTH[@]}" \
-H "If-Match: $ETAG" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"result": "FAILED"
},
"id": "$EXECUTION_ID",
"type": "test_case_executions"
}
}
JSON
# 6. review
curl -sSG "$BESTEST_BASE/api/v1/test_cycles/$CYCLE_ID/testCaseExecutions" \
"${AUTH[@]}" \
--data-urlencode 'fields[test_case_executions]=result,testCaseId' \
| jq .Delete a test case, see it with withDeleted, restore it
Test cases are soft-deleted: DELETE (which, like every write to a locked resource, needs the ETag back as If-Match) returns the tombstone (deletedAt set) with its new ETag, the row stays reachable with withDeleted=true, and the restore action - sent with the tombstone's ETag - clears the tombstone.
Inputs
namestring - A unique test case name within the projectprojectIduuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.
Steps
- 1
createHands on caseId. - 2
readHands on etag. - 3
deleteSends If-Match. Hands on tombstoneEtag. - 4
listDeleted - 5
restoreSends If-Match.
bash, needs curl and jq
#!/usr/bin/env bash
set -euo pipefail
BESTEST_BASE="${BESTEST_BASE:-https://prod-eu.getbestest.com}" # your region
AUTH=(-H "Authorization: Bearer $BESTEST_TOKEN" -H "Accept: application/vnd.api+json")
NAME="API walkthrough $(date +%s)"
PROJECT_ID=$(curl -sS "$BESTEST_BASE/api/v1/projects" "${AUTH[@]}" | jq -r '.data[0].id')
# 1. create
RESPONSE=$(curl -sS -X POST "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
-H "Content-Type: application/vnd.api+json" \
-d @- <<JSON
{
"data": {
"attributes": {
"name": "$NAME",
"projectId": "$PROJECT_ID",
"rank": 1
},
"type": "test_cases"
}
}
JSON
)
echo "$RESPONSE" | jq .
CASE_ID=$(echo "$RESPONSE" | jq -r '.data.id')
# 2. read
curl -sS "$BESTEST_BASE/api/v1/test_cases/$CASE_ID" \
"${AUTH[@]}" \
-D headers.txt \
| jq .
ETAG=$(grep -i '^ETag:' headers.txt | cut -d' ' -f2- | tr -d '\r')
# 3. delete
curl -sS -X DELETE "$BESTEST_BASE/api/v1/test_cases/$CASE_ID" \
"${AUTH[@]}" \
-D headers.txt \
-H "If-Match: $ETAG" \
| jq .
TOMBSTONE_ETAG=$(grep -i '^ETag:' headers.txt | cut -d' ' -f2- | tr -d '\r')
# 4. listDeleted
curl -sSG "$BESTEST_BASE/api/v1/test_cases" \
"${AUTH[@]}" \
--data-urlencode 'withDeleted=true' \
--data-urlencode 'filter=deletedAt IS NOT NULL' \
--data-urlencode 'page[size]=5' \
| jq .
# 5. restore
curl -sS -X POST "$BESTEST_BASE/api/v1/test_cases/$CASE_ID/actions/restore" \
"${AUTH[@]}" \
-H "If-Match: $TOMBSTONE_ETAG" \
| jq .