Skip to main content

API Quality Gate

Analyse a single request, a folder, or an entire collection against 52 automated rules spanning 7 quality dimensions - Functional, Security, Performance, Design, Observability, Test Coverage, and Maintainability. Get a weighted 0-100 score, a PASS / CONDITIONAL PASS / FAIL verdict, a per-issue breakdown with concrete fixes, and a separate CI/CD gate status you can wire into a build pipeline.

πŸ“‚ Opening the Quality Gate panel​

Entry pointResulting scope
Right-click a Collection, Folder, or Request β†’ Tools β†’ API Quality GateWhole Collection / Folder (you choose which requests to include), or a single Request
Quality Gate

The panel opens with three tabs: Rules (choose which checks run), Requests (choose which requests are analysed - collection/folder scope only), and Result (the live report, populated once you run the gate).

πŸ“ Rules tab​

Every rule is listed, grouped by dimension, with a checkbox. Use it to permanently turn rules on/off for this workspace:

  • Per-dimension header shows how many rules are currently selected (e.g. "9/13 selected") and gives Select All / Deselect All buttons for that dimension.
  • Each rule row shows its name, description, and default severity badge.
  • Selections are written to .qgrc.json at the workspace root (disabledRules) as soon as you toggle a checkbox, and are picked up by every run for that workspace.
info

Note: disabling a rule here removes it from evaluation entirely - it produces no issue and isn't counted anywhere, including in exports. If you want a rule to still be evaluated and audited, use an inline @qg-disable tag instead (see Suppressing rules below).

Quality Gate
Expand Rules

Quality Gate
Collapse Rules

πŸ“‹ Requests tab​

  • Single Request scope - this tab simply confirms that request will be analysed; there's nothing to select.
  • Collection / Folder scope - a checkbox table lists every request in scope (method, name, URL). Use Select All / Deselect All, or check individual rows. Run Gate stays disabled until at least one request is selected.
Quality Gate

πŸ“Š Result tab​

  1. Go to the Rules tab and enable/disable checks as needed (optional - all rules run by default).
  2. Go to the Requests tab and confirm/select which requests to include (collection/folder scope only).
  3. Click Run Gate in the footer. You're switched to the Result tab automatically.
  4. While running, a spinner shows "Analysing API quality…".
  5. When finished, the aggregate banner, summary table, and per-request breakdown populate.
  6. Use the Export dropdown in the footer to save the report once it's available.
Quality Gate

🧭 Aggregate banner​

  • A score ring shows the aggregate score (0-100), colour-coded: green β‰₯ 85, orange 70-84, red < 70.
  • A verdict badge (βœ… PASS / ⚠️ CONDITIONAL PASS / ❌ FAIL) for the whole run.
  • A CI Gate badge (PASSED / FAILED) - hover it to see the specific reasons it failed, if any. This is independent of the verdict badge; see CI/CD gate status.
  • Request count and aggregate score.

πŸ“‹ Summary table​

One row per request, one column per dimension, plus Overall and Verdict. Each score cell shows an icon and colour: βœ… green (β‰₯ 85), ⚠️ orange (70-84), ❌ red (< 70) - so you can scan for weak spots across an entire collection at a glance.

🧩 Breakdown (per request)​

Click a request to expand it:

  • Severity pills - Critical / High / Medium / Low counts for that request.
  • Dimension cards - one per dimension, each with its own score bar and issue count. Expand a card to see:
    • The full list of issues, each with severity, description, and (click to expand) impact, recommendation, and a suggested fix where one applies.
    • A Passed Rules / Failed Rules list, so you can see exactly which checks ran and which side of the line each landed on.
  • A note when any issues were suppressed by an inline @qg-disable tag, with a count.

🎯 The 7 quality dimensions​

DimensionDefault weightWhat it evaluates
Functional0.20Whether the request actually works: reachable, no 4xx/5xx, has a response body, test assertions defined and passing
Security0.20Auth, secrets, headers, CORS, and leaked data - mapped to relevant OWASP API Security Top 10 items
Performance0.15Response time, payload size, caching, compression, pagination
Design0.15REST/URL conventions - casing, versioning, verbs, resource identifiers, content types, error shapes
Observability0.10Whether responses are debuggable - correlation IDs, content type, structured error messages/codes
Test Coverage0.10Whether the request has assertions covering status, body, headers, and timing, and whether they pass
Maintainability0.10Naming, documentation, and use of variables instead of hardcoded values/tokens

Weights are configurable per workspace via .qgrc.json (see Configuration) and must sum to 1 for the overall score to stay on a 0-100 scale.

βš–οΈ Scoring model​

Each failed rule deducts points from its dimension's score, based on the issue's severity:

SeverityDeduction
Critical-40
High-20
Medium-10
Low-5

A dimension's score is 100 - (sum of deductions for its issues), floored at 0. The overall score is the weighted average of all seven dimension scores, rounded to the nearest whole number.

🚦 Verdicts​

VerdictCondition
❌ FAILAny Critical-severity issue exists or overall score is below the conditional-pass threshold (default 70)
⚠️ CONDITIONAL PASSNo Critical issues, and score is at or above 70 but below the pass threshold (default 85)
βœ… PASSNo Critical issues, and score is at or above 85

A single Critical issue always forces a FAIL, regardless of the overall score - this can't be overridden by weights.

For a collection/folder run, the aggregate verdict is computed the same way, using the average of every request's score and whether any request has a Critical issue.

πŸ›‘ CI/CD gate status​

gateStatus is a separate pass/fail signal, independent of the PASS/CONDITIONAL PASS/FAIL verdict, meant for build pipelines:

FieldMeaning
passedtrue/false
exitCode0 if passed, 1 if not - use directly as a process exit code in CI scripts
reasonsHuman-readable list of what failed the gate

By default the gate fails if there's more than 0 Critical issues across the whole report, or the aggregate score is below the conditional-pass threshold (70). Both are configurable via failOn in .qgrc.json:

failOn fieldDefaultMeaning
critical0Max Critical issues allowed (report-wide) before the gate fails
highunlimitedMax High-severity issues allowed (report-wide)
minScoreequal to thresholds.scoring.conditionalScoreMinimum aggregate score required

πŸ”‡ Suppressing rules​

There are two independent ways to turn a rule off, and they behave differently:

MethodWhereEffect
Rules tab checkbox / .qgrc.json β†’ disabledRulesWorkspace-wide, persistentRule is skipped entirely - not evaluated, no issue, nothing recorded, excluded from every export
Inline @qg-disable tagIn a single request's Notes fieldRule is still evaluated. If it would have failed, the issue is recorded with suppressed: true - excluded from scoring and verdicts, but kept visible in the UI and exports for audit purposes

Inline tag syntax (place anywhere in the request's Notes):

@qg-disable security/no-auth-mutation   // one specific rule
@qg-disable security // an entire dimension
@qg-disable * // every rule, for this request only

πŸ“š Rule reference​

Functional (7 rules)​

RuleDefault severityChecks
Response receivedHighThe request has been run at least once and has a recorded response
No network errorsCriticalStatus code isn't 0 / no connection error
No server errorsCriticalStatus isn't 5xx
Request completed successfullyHighStatus isn't 4xx
Test assertions definedMediumAt least one test assertion is configured
All test assertions passedHighNo configured test assertion is currently failing
Successful response contains bodyMediumA 2xx (non-204/304) response on a non-HEAD/DELETE request isn't empty

Security (13 rules)​

RuleDefault severityChecks
HTTPS usedCriticalAuth isn't configured on a plain http:// URL
No secrets in query parametersHighNo API key/token/secret/password passed as a query param
No sensitive data in URLCriticalNo password/secret/token/credential keyword in the URL path
Authentication configuredHighPOST/PUT/PATCH/DELETE (non-GraphQL) endpoints have auth configured
Bearer token uses variablesHighA configured Bearer token isn't a hardcoded value
Avoid Basic AuthenticationMediumRequest doesn't use Basic Auth
Response contains x-content-type-optionsMediumResponse includes X-Content-Type-Options: nosniff
Response contains x-frame-optionsMediumResponse includes X-Frame-Options
Response contains strict-transport-securityMediumResponse includes HSTS header
No stack traces in responseHighResponse body doesn't contain a stack-trace pattern
No sensitive secrets in response bodyCriticalBody doesn't match AWS keys, JWTs, private keys, or secret/password fields
No CORS wildcard on mutationsMediumAccess-Control-Allow-Origin: * isn't used on a non-GET response
No CORS wildcard with credentialsCriticalWildcard origin isn't combined with Access-Control-Allow-Credentials: true

Performance (7 rules)​

RuleDefault severityChecks
Response within timeoutCriticalDuration is below the timeout threshold (default 5000 ms)
Response time acceptableMedium (escalates to High)Duration is below the "slow" (500 ms) / "very slow" (2000 ms) thresholds
Payload size optimizedMedium (escalates to High)Response size is below the warn (100 KB) / fail (1 MB) thresholds
Cache-Control configuredMediumGET responses include Cache-Control
Conditional caching enabledLowGET responses include ETag or Last-Modified
Response compression enabledMediumGET responses over the compression threshold (10 KB) include Content-Encoding
Pagination implementedMediumList-style endpoints (/list, /all, /items, /results, /search) accept a pagination parameter

Design (10 rules)​

RuleDefault severityChecks
Lowercase URL pathMediumPath contains no uppercase letters
Use hyphens instead of underscoresLowPath segments don't use underscores
API versioning implementedMediumURL path or Api-Version header indicates a version
GET request has no bodyHighGET requests don't send a body
GET endpoint is read-onlyHighGET URLs don't contain mutation verbs (create/update/delete/…)
Resource-oriented URL namingMediumPath doesn't use verb-style segments (/getUser, …)
Clean resource URLsMediumURL has no technology-specific extension (.php, .asp, .jsp, …)
DELETE targets a specific resourceMediumDELETE path includes a resource identifier
JSON request declares Content-TypeMediumA JSON body is sent with a matching Content-Type header
Standard error response formatLowNon-GraphQL 4xx/5xx bodies follow RFC 7807 or { code, message }

Observability (4 rules)​

RuleDefault severityChecks
Correlation ID returnedMediumResponse includes a correlation/trace header (X-Request-ID, X-Correlation-ID, traceparent, Request-ID)
Content-Type header returnedMediumResponse includes Content-Type
Error response includes messageLow4xx/5xx JSON bodies include message, error, or title
Error response includes codeLow4xx/5xx JSON bodies include code or errorCode

Test Coverage (6 rules)​

RuleDefault severityChecks
Tests definedHighAt least one test assertion is configured
Status code assertionMediumA "Response Code" assertion exists
Response body assertionMediumA body / JSON-body / custom assertion exists
Response time assertionLowA "Response Time" assertion exists
Response header assertionLowA "Response Header" assertion exists
All tests passedCriticalNo configured assertion is currently failing

Maintainability (5 rules)​

RuleDefault severityChecks
Request has a meaningful nameMediumRequest name isn't empty
Request contains documentationLowNotes/description field isn't empty
URL uses environment variablesMediumURL contains at least one {{variable}}
Authentication uses variablesHighA configured Bearer token isn't hardcoded
No empty headersLowNo enabled header has an empty key or value

βš™οΈ Configuration (.qgrc.json)​

Placed at the root of your workspace, this file is optional - every field falls back to a sensible default, and the Rules tab writes to it automatically when you toggle checkboxes.

{
"thresholds": {
"performance": {
"slowMs": 500,
"verySlowMs": 2000,
"timeoutMs": 5000,
"payloadWarnBytes": 102400,
"payloadFailBytes": 1048576,
"compressionThresholdBytes": 10240
},
"scoring": {
"passScore": 85,
"conditionalScore": 70
}
},
"weights": {
"Security": 0.3,
"Functional": 0.2
},
"disabledRules": ["design/underscore-path"],
"failOn": {
"critical": 0,
"high": 5,
"minScore": 75
}
}
SettingDefaultDescription
thresholds.performance.slowMs500Response time (ms) above which a request is flagged Medium
thresholds.performance.verySlowMs2000Response time (ms) above which a request is flagged High
thresholds.performance.timeoutMs5000Response time (ms) above which a request is flagged Critical
thresholds.performance.payloadWarnBytes102400 (100 KB)Payload size above which a Medium warning is raised
thresholds.performance.payloadFailBytes1048576 (1 MB)Payload size above which a High warning is raised
thresholds.performance.compressionThresholdBytes10240 (10 KB)GET response size above which missing compression is flagged
thresholds.scoring.passScore85Minimum overall score for a PASS verdict
thresholds.scoring.conditionalScore70Minimum overall score to avoid FAIL
weightssee dimensions tablePer-dimension weighting of the overall score - only override the dimensions you want to change; others keep their default
disabledRules[]Rule ids, dimension names, or "*" to skip entirely (same as the Rules tab)
failOn.critical0Max Critical issues allowed before the CI gate fails
failOn.highunlimitedMax High issues allowed before the CI gate fails
failOn.minScoreequal to conditionalScoreMinimum aggregate score required for the CI gate to pass

You don't need to include every field - only what you want to override.

πŸ§ͺ How requests are evaluated​

  • If a request has a saved response (from a previous manual run), the gate analyses that response - nothing is re-executed.
  • If a request has no saved response, it's run live as part of the gate, through a shared runner (runLiveQualityGateRequest) that performs the full pipeline in one go:
    1. Its configured pre-request chain runs first - collection/folder-level, then request-level (e.g. token setup) - and any variable updated by that chain is picked up before the main request fires.
    2. The main request fires directly, and only its own duration is measured and used for the Performance dimension.
    3. If the request has test assertions configured, they're executed against the live response and attached as testResults - so Functional β†’ All test assertions passed and every Test Coverage rule reflect the real, current outcome, not just whatever was last saved.
    4. If the request has set-variable rules configured, they run against the live response, same as a normal manual Run.

This runner is shared between the extension's Quality Gate panel and the fc-cli qc command-line tool, so a live-evaluated request behaves identically - same pre-requests, same test execution, same variable updates - whichever surface triggers it.

πŸ“€ Exporting results​

Once a report is available, use the Export dropdown in the footer:

FormatBest for
JSONA complete record of the run - config used, aggregate metrics, every dimension/rule/issue, and suppressed issues. Good for archiving or feeding into another tool.
CSVSummary metrics in a spreadsheet-friendly format, for quick sharing or comparing runs.
HTMLA shareable, readable report you can open in a browser or send to teammates.
XMLA structured export for tools that expect XML.

⚠️ Limitations​

  • The gate reflects the response as recorded or as observed from your machine during a live run - it isn't a substitute for server-side APM or a dedicated security scanner.
  • Rules disabled via .qgrc.json / the Rules tab are skipped entirely and never appear in results or exports; only inline @qg-disable tags are tracked as suppressed issues.
  • A live-evaluated request that has set-variable rules will update your variable set as a side effect of running the gate, just like a normal manual Run would.
  • Weights should sum to 1 to keep the overall score on a true 0-100 scale; the panel doesn't enforce this if you override them in .qgrc.json.