Skip to content

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 require quality:write, which implies quality: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|archived
POST .../quality/plans
GET .../quality/plans/{planId}
PATCH .../quality/plans/{planId}
POST .../quality/plans/{planId}/transitions
GET .../quality/plans/{planId}/suites
POST .../quality/plans/{planId}/suites
GET .../quality/plans/{planId}/suites/{suiteId}
PATCH .../quality/plans/{planId}/suites/{suiteId}
DELETE .../quality/plans/{planId}/suites/{suiteId}
GET .../quality/cases
POST .../quality/cases
GET .../quality/cases/{caseId}
PATCH .../quality/cases/{caseId}
POST .../quality/cases/{caseId}/retire
  • Plans move draft → active → completed → archived; completed → active reopens a plan and archived is terminal. Only active plans accept runs.
  • Suites are never referenced across plans; deleting a suite requires that no run targets it. Cases are never deleted — retire keeps historical evidence resolvable.
  • Plan, suite, and case mutations are idempotent (Idempotency-Key) and take an exact If-Match ETag ("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).
PUT .../quality/cases/{caseId}/work-items/{workItemKey}
DELETE .../quality/cases/{caseId}/work-items/{workItemKey}
GET .../quality/work-items/{workItemKey}/verification
POST .../quality/work-items/{workItemKey}/verification/signoff
  • The link is an idempotent upsert of the requiredForVerification flag 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/runs
GET .../quality/runs/{runId}
POST .../quality/runs/{runId}/transitions
GET .../quality/runs/{runId}/results
POST .../quality/runs/{runId}/results
GET .../quality/runs/{runId}/results/{resultId}/evidence
POST .../quality/runs/{runId}/results/{resultId}/evidence
GET .../quality/runs/{runId}/results/{resultId}/defects
POST .../quality/runs/{runId}/results/{resultId}/defects
DELETE .../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 supersedesResultId to 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 as testerKind: "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 .../gate creates the first configuration without an If-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_FAILED and every unmet condition under details.unmet; the Release Version state is unchanged. Overrides are exceptional: retry the release transition with a mandatory qualityGateOverrideReason (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-store and carries X-Request-Id. Collection reads use {items, page} with opaque cursors.
  • Mutations require an Idempotency-Key and 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 distinguishing web, public_api, and automation sources.
  • 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.