Skip to Content
FeaturesQuality Gate API

Quality Gate API

All endpoints are tenant-scoped via session / API key. Org admins (ORG_OWNER, ORG_ADMIN) or SUPER_ADMIN only for PUT/POST to /quality/gates/config, waivers, and baseline rebuild.

Base URL: https://codestax.co/api (or your self-hosted equivalent).

Auth: same JWT / API key as the rest of the platform — send Cookie (dashboard) or Authorization: Bearer <JWT> (CI/CD).

Quality Gate Config

GET /quality/gates/config

Returns the current org’s policy. Missing row returns defaults with configured: false.

GET /api/quality/gates/config Cookie: ... HTTP/1.1 200 OK { "org_id": 42, "enabled": true, "block_merge_on_fail": false, "applies_to_new_code_only": true, "policy": { "new_critical_max": 0, "new_high_max": 2, "duplication_pct_max": 3.0, "complexity_new_max": 15, "rating_min": "C", "new_coverage_min": 80.0 }, "policy_version": "v1", "configured": true }

PUT /quality/gates/config

Admin only. Upsert the org’s policy.

PUT /api/quality/gates/config Content-Type: application/json { "policy": { "new_critical_max": 0, "new_high_max": 0, "new_coverage_min": 85, "rating_min": "B" }, "enabled": true, "block_merge_on_fail": false, "applies_to_new_code_only": true } HTTP/1.1 200 OK { "org_id": 42, "saved": true, "policy": { ... } }

Unknown policy keys are dropped; unset keys fall back to defaults.

Gate Status Per Scan

GET /quality/gates/status/:scan_id

Returns the precomputed gate status (set by the scanner at scan-completion).

GET /api/quality/gates/status/12345 HTTP/1.1 200 OK { "scan_id": 12345, "status": "failed", "violations": [ { "rule": "new_critical_max", "threshold": 0, "actual": 2, "severity": "critical" }, { "rule": "new_coverage_min", "threshold": 80, "actual": 72.4, "severity": "medium" } ], "default_branch": "main", "scanned_branch": "feature/auth-rework", "is_baseline_scan": false, "new_in_pr": { "critical": 2, "high": 1, "medium": 4, "low": 0, "total": 7 } }

statuspassed | failed | not_configured.

POST /quality/gates/evaluate/:scan_id

Re-evaluate findings against the current org policy without writing. Useful when you change policy and want to preview the impact on historical scans.

POST /api/quality/gates/evaluate/12345 HTTP/1.1 200 OK { "status": "passed", "violations": [], "waived_violations": [ { "rule": "duplication_pct_max", "threshold": 3, "actual": 4.2, "severity": "high", "waived": true } ], "policy_version": "v1", "policy": { ... } }

Waivers

GET /quality/gates/waivers

List waivers for the current org. Query params: include_expired=false, include_revoked=false, repo_id=<int>.

GET /api/quality/gates/waivers?include_expired=true HTTP/1.1 200 OK { "org_id": 42, "waivers": [ { "id": 7, "rule": "duplication_pct_max", "reason": "Legacy monolith — scheduled refactor ticket #1234", "repo_id": 8, "pr_number": null, "created_by_user_id": 15, "created_at": "2026-04-15T10:00:00Z", "expires_at": "2026-06-01T00:00:00Z", "revoked_at": null, "expired": false, "revoked": false } ] }

POST /quality/gates/waivers

Admin only. Create a waiver.

POST /api/quality/gates/waivers Content-Type: application/json { "rule": "new_critical_max", "reason": "CVE-2026-XXXX is in a dev-only test harness, not deployed", "repo_id": 8, "pr_number": 142, "expires_at": "2026-05-01T00:00:00Z" } HTTP/1.1 200 OK { "id": 8, "rule": "new_critical_max", "repo_id": 8, "pr_number": 142, "created_at": "2026-04-19T12:00:00Z", "expires_at": "2026-05-01T00:00:00Z" }

Scope ladder: specific PR → specific repo → org-wide. Most-specific match wins during evaluation.

POST /quality/gates/waivers/:id/revoke

Admin only. Soft-delete a waiver — stops honoring it on the next scan.

POST /api/quality/gates/waivers/8/revoke HTTP/1.1 200 OK { "id": 8, "revoked": true, "revoked_at": "2026-04-19T14:00:00Z" }

Baseline Rebuild

POST /quality/gates/rebuild-baseline/:repo_id

Admin only. Clears the default-branch QualityBaseline fingerprints for this repo. Next default-branch scan repopulates the baseline. Use after a big refactor.

POST /api/quality/gates/rebuild-baseline/8 HTTP/1.1 200 OK { "repo_id": 8, "branch": "main", "cleared_fingerprints": 847, "note": "Next scan on the default branch will re-establish the baseline." }

Quality Insights

GET /quality/ratings/:scan_id

GET /api/quality/ratings/12345 HTTP/1.1 200 OK { "scan_id": 12345, "quality_rating": "B", "maintainability_rating": "B", "reliability_rating": "A", "coverage_rating": "C", "duplication_rating": "A", "tech_debt_minutes": 847, "parse_coverage_pct": 98.2, "files_parsed": 1847, "files_skipped": 34, "tool_versions": { "lizard": "1.17.13", "semgrep": "1.95.0", ... } }

GET /quality/tech-debt/:scan_id

GET /api/quality/tech-debt/12345 HTTP/1.1 200 OK { "scan_id": 12345, "total_minutes": 847, "total_hours": 14.12, "total_engineer_days": 1.76, "findings_by_type": { "complexity": 23, "dead_code": 8, "duplication": 5, "coverage_gap": 12 } }

Coverage

POST /quality/coverage/upload

Upload a coverage report. Multipart form: scan_id (required, int), report_format (optional — lcov / cobertura / jacoco / clover, auto-detected if absent), file (required). Max 25 MB.

curl -X POST https://codestax.co/api/quality/coverage/upload \ -H "Authorization: Bearer $JWT" \ -F "scan_id=12345" \ -F "report_format=lcov" \ -F "file=@coverage/lcov.info"
{ "scan_id": 12345, "records_persisted": 847 }

GET /quality/coverage/:scan_id

GET /api/quality/coverage/12345 HTTP/1.1 200 OK { "scan_id": 12345, "file_count": 847, "lines_total": 42100, "lines_covered": 38700, "coverage_pct": 91.92, "files": [ { "file_path": "src/auth/handler.py", "lines_total": 420, "lines_covered": 412, "lines_missed": 8, "coverage_pct": 98.1, "report_format": "lcov" } ] }

Mark False Positive

POST /quality/findings/mark-fp

Tenant-scoped (verified against scan → repo → org chain).

POST /api/quality/findings/mark-fp Content-Type: application/json { "issue_id": 98765, "is_fp": true, "reason": "Sanitized via framework encoder at the boundary" } HTTP/1.1 200 OK { "issue_id": 98765, "user_marked_fp": true, "fp_marked_at": "2026-04-19T15:00:00Z", "fp_reason": "..." }

Send is_fp: false to unmark.

CI/CD Usage

Use /quality/gates/status/:scan_id in your pipeline to block deployment on gate failure. Example (GitHub Actions):

- name: Wait for CodeStax scan + check gate run: | SCAN_ID="${{ steps.trigger.outputs.scan_id }}" until [ "$(curl -sf -H "Authorization: Bearer $CODESTAX_JWT" \ "https://codestax.co/api/quality/gates/status/$SCAN_ID" | jq -r .status)" != "null" ]; do sleep 10 done STATUS=$(curl -sf -H "Authorization: Bearer $CODESTAX_JWT" \ "https://codestax.co/api/quality/gates/status/$SCAN_ID" | jq -r .status) echo "Gate: $STATUS" [ "$STATUS" = "passed" ] || [ "$STATUS" = "not_configured" ] || exit 1

Error Responses

StatusWhen
401No session / invalid JWT / revoked API key
403Not an org admin for config / waivers / rebuild-baseline; non-super-admin crossing tenants
404Scan / waiver / repo not found in caller’s org
400Policy JSON malformed; coverage upload > 25 MB; unparseable format
429Rate-limited (per-route; login routes have stricter caps)