REST API reference

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.

Read only

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. 1

    anyFailed

  2. 2

    allUnexecuted

  3. 3

    nonePassed

Script

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 .
Read only

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. 1
Script

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 .
Read only

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

  • cursor string - The page[after] value of the previous response's links.next

Steps

  1. 1

    firstPage Hands on next.

  2. 2
Script

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 only

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. 1

    pick Hands on caseId.

  2. 2
  3. 3
  4. 4

    idsOnly - The same related route narrowed to identifiers only - the id-list form (there is no separate linkage route).

Script

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 .
Read only

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. 1

    me Hands on userId.

  2. 2
Script

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 .
Writes data

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

  • name string - A unique test case name within the project
  • projectId uuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.

Steps

  1. 1

    createCase Hands on caseId.

  2. 2

    firstStep

  3. 3

    secondStep

  4. 4
Script

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 .
Writes data

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

  • name string - A unique name for the cycle and the case
  • projectId uuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.

Steps

  1. 1

    createCycle Hands on cycleId.

  2. 2

    createCase Hands on caseId.

  3. 3

    addExecution Hands on executionId.

  4. 4

    readExecution Hands on etag.

  5. 5

    recordResult Sends If-Match.

  6. 6
Script

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 .
Writes data

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

  • name string - A unique test case name within the project
  • projectId uuid - your Space id. The script reads it from GET /projects, which returns the one Space your token reaches.

Steps

  1. 1

    create Hands on caseId.

  2. 2

    read Hands on etag.

  3. 3

    delete Sends If-Match. Hands on tombstoneEtag.

  4. 4

    listDeleted

  5. 5

    restore Sends If-Match.

Script

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 .