zephyr-scale-case-management
Author and manage Zephyr Scale Cloud test cases via the REST API v2 - create tests, attach steps, link to Jira issues, organise into folders, manage test cycles. Covers Bearer-token auth, the /testcases endpoints, the testScript / steps shape, and folder hierarchy. Use for pre-execution case authoring in Jira-anchored teams using Zephyr Scale (formerly TM4J). Distinct from Zephyr's test-cycle / execution endpoints which post results.
Install with skills.sh (any agent)
npx skills add testland/qa --skill zephyr-scale-case-managementzephyr-scale-case-management
Overview
Zephyr Scale Cloud (formerly Adaptavist TM4J, now SmartBear) exposes a REST API v2 with Bearer-token authentication.
Per smartbear.com/test-management/zephyr-scale (Cloudflare- protected; cite by stable URL).
For canonical anatomy, see test-case-anatomy-reference.
When to use
Authoring
Authentication
Zephyr Scale Cloud uses Bearer tokens generated from the Zephyr Scale UI (API access tokens, per-user):
export ZS_TOKEN="<bearer-token-from-zephyr-scale-ui>"import requests, os
BASE = "https://api.zephyrscale.smartbear.com/v2"
HEADERS = {
"Authorization": f"Bearer {os.environ['ZS_TOKEN']}",
"Content-Type": "application/json",
}Create a test case
POST /testcases:
def create_test_case(project_key, name, objective=None, precondition=None,
steps=None, owner=None, folder_id=None,
labels=None, components=None, priority="Normal",
status="Approved"):
"""
steps: list of {"inline": {"description": "...", "expectedResult": "..."}}
priority: Highest / High / Normal / Low / Lowest (per project config)
status: Approved / Draft / Deprecated
"""
body = {
"projectKey": project_key,
"name": name,
"objective": objective,
"precondition": precondition,
"ownerId": owner,
"folderId": folder_id,
"labels": labels or [],
"componentId": components,
"priorityName": priority,
"statusName": status,
}
r = requests.post(f"{BASE}/testcases", json=body, headers=HEADERS)
r.raise_for_status()
created = r.json()
if steps:
attach_steps(created["key"], steps)
return createdTest script + steps
Steps are managed via a separate testScript endpoint:
def attach_steps(test_case_key, steps):
"""
steps: list of {"inline": {"description": str, "expectedResult": str,
"testData": str}}
"""
body = {"mode": "OVERWRITE", "items": [{"inline": s} for s in steps]}
r = requests.post(f"{BASE}/testcases/{test_case_key}/teststeps",
json=body, headers=HEADERS)
r.raise_for_status()
return r.json()
steps = [
{"description": "Navigate to /login",
"expectedResult": "Login form rendered", "testData": ""},
{"description": "Enter credentials and click Submit",
"expectedResult": "Redirected to /dashboard",
"testData": "alice@example.com / pw123"},
]
attach_steps("PROJ-T123", steps)mode: OVERWRITE replaces the existing step list; APPEND adds to it.
Folders + Jira links
create_folder (folders nest; build the hierarchy first, then place cases) and link_to_jira (resolve the Jira key -> issue ID via the Jira REST API first) are in references/zephyr-scale-api-reference.md, alongside the migration field map and response shapes.
Get + list
case = requests.get(f"{BASE}/testcases/PROJ-T123", headers=HEADERS).json()
# Paginated list
def list_cases(project_key, max_results=100):
cases = []
start_at = 0
while True:
r = requests.get(f"{BASE}/testcases",
params={"projectKey": project_key,
"startAt": start_at,
"maxResults": max_results},
headers=HEADERS)
r.raise_for_status()
data = r.json()
cases.extend(data["values"])
if data["isLast"]:
break
start_at += max_results
return casesResponse shape: {"values": [...], "startAt", "maxResults", "total", "isLast"}.
Running
Bulk import via CSV to JSON
Wrap each row in try/except, throttle to ~60 req/min (the tenant limit), and tally successes so one failing row does not abort the batch:
import csv, time
rows = list(csv.DictReader(open("legacy.csv")))
created, failed = [], []
for row in rows:
try:
case = create_test_case(
project_key=row["project"],
name=row["title"],
objective=row.get("objective"),
precondition=row.get("precondition"),
priority=row.get("priority", "Normal"),
labels=row.get("labels", "").split(",") if row.get("labels") else None,
)
steps = [
{"description": s, "expectedResult": e, "testData": d}
for s, e, d in zip(
row["steps"].split("|"),
row["expected"].split("|"),
(row.get("data") or "").split("|"),
)
]
attach_steps(case["key"], steps)
created.append(case["key"])
except Exception as e:
failed.append((row["title"], str(e)))
time.sleep(1) # ~60 req/min; a 429 means back off further
print(f"created {len(created)}, failed {len(failed)}")Verify: assert len(created) + len(failed) == len(rows) and that failed is empty before treating the import as complete. On any 429, increase the delay; on other errors, fix the source row and re-run the failed titles.
The migration field map (source column -> Zephyr field) is in references/zephyr-scale-api-reference.md.
Parsing results
create_test_case returns {"id", "key", "self"}. The key is the project-prefixed identifier (PROJ-T123). Build permalink:
# Jira Cloud + Zephyr Scale share a tenant
url = f"https://your-tenant.atlassian.net/projects/{project_key}?selectedItem=com.thed.zephyr.tests%3Atestcases#testcase/{key}"CI integration
Sync per-spec front-matter to Zephyr Scale:
- name: Sync to Zephyr Scale
env:
ZS_TOKEN: ${{ secrets.ZEPHYR_SCALE_TOKEN }}
run: python scripts/sync-zephyr-scale.py specs/Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Inline steps in test-case body (objective field) | Per-step pass/fail unavailable | Use /teststeps testScript endpoint |
| Hard-coded priority names | Project-specific enum may differ | Discover via project config endpoint |
| Flat folder hierarchy | Hundreds of cases unfindable | Create folders matching feature areas |
Mixing componentId and labels[] randomly | Cross-team queries break | Component for ownership; labels for cross-cutting concerns |
| Bulk-create without rate throttling | 429s on >100 cases / min | Limit to ~60 req / min |
| Storing the Bearer token in repo | Token leak | Environment variable / secret store |
Limitations
References
Zephyr Scale Cloud API v2 reference
View source (opens in new window)Zephyr Scale Cloud API v2 reference
Migration field map, folder + Jira-link operations, and response shapes for the Zephyr Scale Cloud REST API v2. Per smartbear.com/test-management/zephyr-scale (Cloudflare-protected; cite by stable URL). BASE, HEADERS, and resolve_jira_issue_id are defined in SKILL.md.
Migration target field map
| Source field | Zephyr field |
|---|---|
| Title | name |
| Objective | objective |
| Preconditions | precondition |
| Steps | testScript items |
| Owner | ownerId (Jira user ID) |
| Priority | priorityName (project enum) |
| Status | statusName |
| Labels | labels[] |
| Component | componentId (Jira component) |
| Requirement traceability | /links/issues |
Folders
def create_folder(project_key, name, folder_type="TEST_CASE", parent_id=None): r = requests.post(f"{BASE}/folders", json={ "projectKey": project_key, "name": name, "folderType": folder_type, # TEST_CASE / TEST_PLAN / TEST_CYCLE "parentId": parent_id, }, headers=HEADERS) r.raise_for_status() return r.json()
Folders nest; create the hierarchy first, then place cases.
Linking to Jira issues
def link_to_jira(test_case_key, issue_key): r = requests.post(f"{BASE}/testcases/{test_case_key}/links/issues", json={"issueId": resolve_jira_issue_id(issue_key)}, headers=HEADERS) r.raise_for_status()
Requires the Jira REST API to resolve issue key -> issue ID separately. Trace back from cases to requirements via the linked-issues endpoint.
Response shapes
Related skills
allure-testops-case-management
Author and manage Allure TestOps test cases via the REST API - create cases, manage projects + suites, attach scenarios with nested steps, link to Jira / GitHub issues, sync with allure-results from CI runs. Covers Bearer-token auth, /api/rs/testcase CRUD endpoints, nested-step `scenario` shape, and the unique Allure TestOps feature of linking automated results back to manual case definitions. Use for pre-execution case authoring in teams using Allure TestOps as the canonical TCM.
qase-io-case-management
Author and manage Qase.io test cases via the Public API v1 - create cases, organise into suites, attach structured steps, link to Jira/Linear/GitHub, manage shared steps, and bulk-import via JSON. Covers Token header auth, /case/{project_code} CRUD endpoints, the steps array with action / expected_result / data shape, and shared-step reuse. Use for pre-execution case authoring in teams using Qase as a modern lightweight TCM.
test-case-anatomy-reference
Pure-reference catalog of test-case anatomy - what fields a well-formed test case must have and what each field means. Enumerates the ISO/IEC/IEEE 29119-3:2021 test-case template fields (identifier, objective, preconditions, inputs, steps, expected results, postconditions, environment, traceability) and the ISTQB CTAL-TM specification-technique-driven additions (equivalence partition, boundary value, decision table, state transition). Maps the canonical anatomy to five tracker-specific schemas (TestRail, Xray, Zephyr Scale, Allure TestOps, Qase). Use as the authoritative source when authoring a case template, reviewing case quality, or migrating between tools.
test-case-review-rubric
Scores an already-written test case against six per-case quality axes (objective specificity, precondition executability, step granularity, step abstraction level, expected-result observability, traceability validity) and six set-level axes (partition coverage, boundary coverage, duplication, orphan and uncovered requirements, tier shape, identifier consistency). Derives a per-case PASS / WEAK / FAIL verdict and a set verdict from it without averaging, and marks every threshold as either standard-backed (ISTQB glossary, ISTQB CTFL v4.0, ISO/IEC/IEEE 29119-3:2021) or practitioner convention (step-count ceiling, tier bands, provenance threshold). Assumes the case field list and field cardinality are already defined by a test-case anatomy reference and judges content quality only. Use when reviewing a batch of hand-written test cases before promoting them to a release suite or handing them to an automation engineer.
testrail-case-management
Author and manage test cases in TestRail via REST API v2 - create cases, organise into suites + sections, update steps + expected results, bulk import from CSV/JSON, set automation status, link to references (Jira / requirements). Covers the Steps / Text / Exploratory templates, custom-field discovery (`get_case_fields`), and pagination on `get_cases`. Use for pre-execution case authoring and repository management. Do NOT use for submitting test-run results (pass/fail, status updates): posting results via add_results_for_cases is a separate post-execution concern.
traceability-matrix-builder
Build-an-X workflow that produces a requirements-to-tests traceability matrix from a TCM case repository + a requirements source (Jira / Linear / GitHub Issues). Walks the author through (1) extracting requirements with stable IDs, (2) extracting cases + their refs, (3) computing coverage (which requirements have at least one test, which tests verify which requirements, orphaned cases / orphaned requirements), (4) emitting a CSV / Markdown / HTML matrix, and (5) producing an executive summary (X% requirement coverage, Y orphans, Z over-tested). Use for test coverage audits, finding requirements-coverage gaps, sprint-end coverage reviews, compliance documentation, and traceability in regulated industries.
xray-case-management
Author and manage Xray test cases (Jira issues with Test issue type) via the GraphQL + REST APIs - create tests, attach steps, link preconditions, set testType (Manual / Cucumber / Generic), associate with requirements, bulk import via JSON. Covers OAuth client_id/client_secret auth, the GraphQL createTest mutation, the REST /api/v2/import/test/bulk endpoint, and the Cucumber-style scenario authoring path. Use for pre-execution case authoring in Jira-anchored teams using Xray. Distinct from Xray's test-execution / test-run features which post results.