Quality API
Quality API
Quality management is Hamstik’s system of record for planned verification, execution evidence, defects, regression history, and release quality decisions. Public API v1 exposes the same shared services the web app uses, so counts, verification states, and gate evaluations always agree.
All routes below live under
/api/v1/organizations/{organizationSlug}/projects/{projectKey}/quality and
are Project-scoped: a test relationship can never widen a caller’s reach
beyond the Project the token already has access to.
Scopes and roles
- Reads require
quality:read. Writes requirequality:write, which impliesquality:read. - Scope gates the attempt only. Every mutation re-verifies the live credential, Organization, membership, and Project rows inside its transaction, exactly like every other domain. Definition administration (plans, suites, cases, gate configuration) additionally requires an Organization administrator or owner role; execution (runs, results, evidence, defect links, coverage links, signoffs) is member work.
- The same Organization authorization boundary applies to reads: a PAT sees only Projects its membership and resource grants allow.
Test plans, suites, and cases
GET .../quality/plans ?state=draft|active|completed|archivedPOST .../quality/plansGET .../quality/plans/{planId}PATCH .../quality/plans/{planId}POST .../quality/plans/{planId}/transitionsGET .../quality/plans/{planId}/suitesPOST .../quality/plans/{planId}/suitesGET .../quality/plans/{planId}/suites/{suiteId}PATCH .../quality/plans/{planId}/suites/{suiteId}DELETE .../quality/plans/{planId}/suites/{suiteId}GET .../quality/casesPOST .../quality/casesGET .../quality/cases/{caseId}PATCH .../quality/cases/{caseId}POST .../quality/cases/{caseId}/retire- Plans move
draft → active → completed → archived;completed → activereopens a plan andarchivedis terminal. Onlyactiveplans accept runs. - Suites are never referenced across plans; deleting a suite requires that no
run targets it. Cases are never deleted —
retirekeeps historical evidence resolvable. - Plan, suite, and case mutations are idempotent (
Idempotency-Key) and take an exactIf-MatchETag ("plan-<revision>","suite-<revision>","case-<revision>"). Editing a case bumps its revision only; executed case snapshots captured inside runs never change. - Bounds: 100 plans per Project, 50 suites per plan, 2000 cases per Project, 100 steps per case, 50 results per (run, case).
Coverage links and verification
PUT .../quality/cases/{caseId}/work-items/{workItemKey}DELETE .../quality/cases/{caseId}/work-items/{workItemKey}GET .../quality/work-items/{workItemKey}/verificationPOST .../quality/work-items/{workItemKey}/verification/signoff- The link is an idempotent upsert of the
requiredForVerificationflag and is same-Project only. - Verification state is derived on read — never stored — from the required
linked cases’ latest results and current signoff (
not_required,awaiting_verification,in_verification,verified,verification_failed). Workflow status is never modified by verification activity; there are no custom Work Item statuses. - A signoff records the approver, timestamp, evidence digest, and snapshot at approval time. Rows are immutable and a new signoff supersedes the previous one. Signoff is a real-user action attributed to the PAT’s owning user; automation never signs off.
Runs, results, and machine publishing
GET .../quality/runs ?planId=&state=POST .../quality/runsGET .../quality/runs/{runId}POST .../quality/runs/{runId}/transitionsGET .../quality/runs/{runId}/resultsPOST .../quality/runs/{runId}/resultsGET .../quality/runs/{runId}/results/{resultId}/evidencePOST .../quality/runs/{runId}/results/{resultId}/evidenceGET .../quality/runs/{runId}/results/{resultId}/defectsPOST .../quality/runs/{runId}/results/{resultId}/defectsDELETE .../quality/runs/{runId}/results/{resultId}/defects/{workItemKey}- Runs target an active plan and optionally one suite or an explicit case
list. Lifecycle is
not_started → in_progress → completed | aborted; completing or aborting never touches recorded results. The first recorded result implicitly starts the run. - Results are append-only immutable evidence. A rerun or amendment creates a
new result; send
supersedesResultIdto reference the row being superseded so the amendment path stays auditable. The current verdict of a case is its latest result (highest sequence). Direct updates and deletes are rejected by the database.
Machine publishing (automation)
Automated systems publish results without impersonating a human tester:
{ "caseId": "5f0b3c1e-…", "outcome": "passed", "automation": { "label": "ci-e2e", "metadata": { "pipeline": "https://ci.example.test/42" } }}automation.label(1–200 characters) is required with the automation object and is stored with the result astesterKind: "automation"plus bounded metadata (≤ 25 entries, scalar values, ≤ 4000 serialized characters). The PAT’s owning user is recorded as the credential context, but a machine result is never presented as a human execution.- Machine publishing creates results only. It never creates signoffs, never modifies evidence, and never drives release signoff conditions.
- The request shape is format-neutral. A JUnit XML (or equivalent) importer could layer on top of this contract later without redesign; importer support itself is out of scope.
Defects
Defects are ordinary Work Items of the existing bug type — no new Work Item
type, status, or field. POST .../defects with mode: "create_bug" creates a
Bug prefilled from the execution context (case, run, outcome, notes, steps) or
mode: "link_bug" with bugKey links an existing one. Unlinking removes only
the link row; the failed result is never modified, so the
case → run → result → defect chain stays navigable after a Bug is resolved.
Release quality gates
GET .../quality/gate ?releaseVersionId=<uuid>PUT .../quality/gate- Gates are opt-in per Project. A Project without a configuration keeps the ordinary release workflow with zero behavior change.
- Conditions (closed set):
required_tests_executed,no_required_tests_failing,no_blocking_tests,required_work_items_verified,quality_signoff_recorded. Up to 10 conditions per Project. GET .../gate?releaseVersionId=…evaluates the gate for one Release Version using the same shared reporting service the UI and gates use, so every surface agrees on the numbers. The response includes each unmet condition with a summary and the Work Items that demonstrate it.PUT .../gatecreates the first configuration without anIf-Match(or with*only while none exists) and requires the exact"gate-<revision>"ETag afterwards.- A gate blocks the release transition with
409 QUALITY_GATE_FAILEDand every unmet condition underdetails.unmet; the Release Version state is unchanged. Overrides are exceptional: retry the release transition with a mandatoryqualityGateOverrideReason(1–2000 characters). The override snapshots the unmet conditions, the reason, the actor, and the gate configuration revision in immutable, trigger-protected history. Overrides are per-release and never retroactively mark conditions met. See Releases for the transition request shape.
Machine behavior
- Every Quality response is
Cache-Control: no-storeand carriesX-Request-Id. Collection reads use{items, page}with opaque cursors. - Mutations require an
Idempotency-Keyand replay the original response on retry. Versioned resources return and require ETags as listed above;*(last-write-wins) is not accepted for Quality resources. - Every mutation writes an
apiAuditEvent, and the Quality domain keeps its own append-only audit stream distinguishingweb,public_api, andautomationsources. - Test-case coverage is not source-code coverage. Execution percentages and pass percentages are always reported as two separate numbers, never conflated into a single “coverage” figure.
