Verification probes are safe requests Qodex runs against a PR preview deployment to check whether a finding is reproducible.They help Qodex move beyond static diff review. If a preview URL exists and the issue can be checked safely, Qodex can run the code and attach request and response evidence.
The agent adds a probe to a finding only when the bug can be checked with one safe GET request against the preview build. Qodex discovers the preview URL through the GitHub Deployments API, checks the URL against safety rules and the repo allowlist, runs the probe, and attaches the result to the inline comment.If the response matches the expected signal, the finding is verified. If the probe runs but does not match, the finding is unverified. If Qodex cannot run a probe, the finding is skipped or failed depending on why.Only verified findings can block a merge when merge gating is enabled.
GET is the only supported method today. Non-idempotent verbs are gated behind an explicit .qodex.yaml opt-in (probes.allow_non_get, defaults to false) and are on the roadmap.
The path must resolve against the discovered preview URL on the same origin. Qodex refuses paths that try to break the origin, including //, http://, javascript:, .. outside the origin, or control characters.
For each PR, Qodex reads the GitHub Deployments API for the head SHA and picks the first successful deployment with a public environment_url or target_url.The candidate URL must pass the global safe-URL check. If the repo defines probes.preview_host_allowlist in .qodex.yaml, the host must match that allowlist too. If no deployment qualifies, the review still runs but probes are skipped.
The probe is agent-controlled, and the agent reads PR content, so Qodex treats every probe as untrusted.Qodex refuses probes that target unsafe or private destinations: HTTP, localhost, private or reserved IP ranges, IPv6 literals, and obfuscated IP encodings are all rejected before any request is made.
Patterns may start with *. to allow a domain and its subdomains. A null allowlist, the default, is permissive, but the global SSRF guard still applies.When the allowlist is set and the discovered host does not match, the review proceeds without probes and the walkthrough explains that the preview deployment was skipped.
When a probe runs, the inline comment includes a probe badge and a collapsible “Probe evidence” block.
Critical **Missing auth on profile endpoint** - _security_ - VerifiedThe endpoint reads the ID from the URL and queries the row withoutchecking the session user.```suggestionif (id !== req.session.userId) return res.status(403).end();```<details><summary>Probe evidence</summary>**Request** `GET https://pr-42.preview.acme.app/api/profile/9999`**Response** `200 OK` in 142ms~~~{"id":"9999","email":"victim@example.com"}~~~**Verdict** response status 200 matched expected 200</details>
Opt-in non-GET probes (POST, PUT, PATCH, DELETE) are on the roadmap, gated by .qodex.yaml’s probes.allow_non_get: true. They are useful for verifying state-mutating bugs on preview environments you are willing to mutate.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Qodex","colors":{"primary":"#3B82F6"},"logo":{"light":"https://mintcdn.com/qodexai/P6C0FF6uddKFEAsX/logo-coral.svg?fit=max\u0026auto=format\u0026n=P6C0FF6uddKFEAsX\u0026q=85\u0026s=4b3af197a898df33ff25e878e4209af8","dark":"https://mintcdn.com/qodexai/P6C0FF6uddKFEAsX/logo-coral.svg?fit=max\u0026auto=format\u0026n=P6C0FF6uddKFEAsX\u0026q=85\u0026s=4b3af197a898df33ff25e878e4209af8","href":"https://qodex.ai/docs/introduction"},"favicon":"https://mintcdn.com/qodexai/P6C0FF6uddKFEAsX/favicon.png?fit=max\u0026auto=format\u0026n=P6C0FF6uddKFEAsX\u0026q=85\u0026s=520beb39c30302f8496f2a2050ee9421","navbar":{"primary":{"type":"button","label":"Dashboard","href":"https://agents.qodex.ai/dashboard"}},"search":{"prompt":"Search the docs..."},"icons":{"library":"lucide"},"contextual":{"options":["copy","view","chatgpt","claude","perplexity"],"display":"header"},"styling":{"latex":true},"navigation":{"global":{"anchors":[{"anchor":"Demo Call with CEO","icon":"phone","href":"https://cal.com/siddhant-mohan/45mins?duration=45\u0026overlayCalendar=true"},{"anchor":"Blog","icon":"newspaper","href":"https://qodex.ai/blog"},{"anchor":"Discord","icon":"message-circle","href":"https://discord.gg/h2tP328g"},{"anchor":"Changelog","icon":"scroll-text","href":"/changelog"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"qodexai","hasAudioNarration":false,"actualSubdomain":"qodexai","customDomains":["qodex.ai"],"gitSource":{"type":"github","owner":"qodex-ai","repo":"docs-public","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6add0c32002829fb71da2c63","collectionId":"77df8ad4-117a-4ed7-8c5b-578b43e4fc0e","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6add0c32002829fb71da2c63"},"entitlements":{},"buildId":"6add0c32002829fb71da2c63","clientVersion":"0.0.4076","searchFilterCounts":[{"language":"default","tag":"Documentation","count":97},{"language":"default","tag":"Reference","count":23},{"language":"default","tag":"Run tests","count":16},{"language":"default","tag":"Common questions","count":13},{"language":"default","tag":"PR review","count":13},{"language":"default","tag":"API testing","count":12},{"language":"default","tag":"OpenQodex","count":12},{"language":"default","tag":"Integrations","count":8},{"language":"default","tag":"Get started","count":7},{"language":"default","tag":"In CI","count":7}]},"assistantConfig":{},"basePath":"/docs","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"comment-mode","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"pr-review-verification-probes","slug":"/pr-review-verification-probes","pageMetadata":{"title":"Verification probes","description":"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.","icon":"zap","href":"/pr-review-verification-probes"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={code:`code`,li:`li`,p:`p`,pre:`pre`,span:`span`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{Card,CardGroup,CodeBlock,Heading,Table,Tip}=_components;if(!Card)_missingMdxReference(`Card`,true);if(!CardGroup)_missingMdxReference(`CardGroup`,true);if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Tip)_missingMdxReference(`Tip`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`1`,id:`verification-probes`,children:`Verification probes`}),`\n`,_jsx(_components.p,{children:`Verification probes are safe requests Qodex runs against a PR preview deployment to check whether a finding is reproducible.`}),`\n`,_jsx(_components.p,{children:`They help Qodex move beyond static diff review. If a preview URL exists and the issue can be checked safely, Qodex can run the code and attach request and response evidence.`}),`\n`,_jsx(Heading,{level:`2`,id:`how-probes-work`,children:`How probes work`}),`\n`,_jsx(_components.p,{children:`The agent adds a probe to a finding only when the bug can be checked with one safe GET request against the preview build. Qodex discovers the preview URL through the GitHub Deployments API, checks the URL against safety rules and the repo allowlist, runs the probe, and attaches the result to the inline comment.`}),`\n`,_jsxs(_components.p,{children:[`If the response matches the expected signal, the finding is `,_jsx(_components.code,{children:`verified`}),`. If the probe runs but does not match, the finding is `,_jsx(_components.code,{children:`unverified`}),`. If Qodex cannot run a probe, the finding is `,_jsx(_components.code,{children:`skipped`}),` or `,_jsx(_components.code,{children:`failed`}),` depending on why.`]}),`\n`,_jsx(_components.p,{children:`Only verified findings can block a merge when merge gating is enabled.`}),`\n`,_jsx(Heading,{level:`2`,id:`probe-spec`,children:`Probe spec`}),`\n`,_jsx(_components.p,{children:`A probe spec belongs to a single finding in the review output. The shape is intentionally narrow.`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`5`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`5`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"method\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"GET\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"path\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"/api/users/9999\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"expected_signal\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: { `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:`\"type\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"status_code\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:`\"value\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0550AE`,\"--shiki-dark\":`#B5CEA8`},children:`200`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`method`,children:`Method`}),`\n`,_jsxs(_components.p,{children:[`GET is the only supported method today. Non-idempotent verbs are gated behind an explicit `,_jsx(_components.code,{children:`.qodex.yaml`}),` opt-in (`,_jsx(_components.code,{children:`probes.allow_non_get`}),`, defaults to `,_jsx(_components.code,{children:`false`}),`) and are on the roadmap.`]}),`\n`,_jsx(Heading,{level:`2`,id:`path`,children:`Path`}),`\n`,_jsxs(_components.p,{children:[`The path must resolve against the discovered preview URL on the same origin. Qodex refuses paths that try to break the origin, including `,_jsx(_components.code,{children:`//`}),`, `,_jsx(_components.code,{children:`http://`}),`, `,_jsx(_components.code,{children:`javascript:`}),`, `,_jsx(_components.code,{children:`..`}),` outside the origin, or control characters.`]}),`\n`,_jsx(Heading,{level:`2`,id:`expected-signal`,children:`Expected signal`}),`\n`,_jsx(_components.p,{children:`The expected signal describes what the bug produces, not what the correct behavior produces. A match means the bug is reproducible.`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Type`}),_jsx(_components.th,{children:`Match condition`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`status_code`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`response.status === value`})})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`status_range`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`response.status`}),` between `,_jsx(_components.code,{children:`min`}),` and `,_jsx(_components.code,{children:`max`}),` inclusive`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`body_contains`})}),_jsx(_components.td,{children:_jsx(_components.code,{children:`response.body.includes(value)`})})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`header_present`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`response.headers`}),` has a key matching `,_jsx(_components.code,{children:`name`}),` (case-insensitive)`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`header_value`})}),_jsxs(_components.td,{children:[_jsx(_components.code,{children:`response.headers[name] === value`}),` (case-insensitive key)`]})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`preview-url-discovery`,children:`Preview URL discovery`}),`\n`,_jsxs(_components.p,{children:[`For each PR, Qodex reads the GitHub Deployments API for the head SHA and picks the first successful deployment with a public `,_jsx(_components.code,{children:`environment_url`}),` or `,_jsx(_components.code,{children:`target_url`}),`.`]}),`\n`,_jsxs(_components.p,{children:[`The candidate URL must pass the global safe-URL check. If the repo defines `,_jsx(_components.code,{children:`probes.preview_host_allowlist`}),` in `,_jsx(_components.code,{children:`.qodex.yaml`}),`, the host must match that allowlist too. If no deployment qualifies, the review still runs but probes are skipped.`]}),`\n`,_jsx(Heading,{level:`2`,id:`security-guards`,children:`Security guards`}),`\n`,_jsx(_components.p,{children:`The probe is agent-controlled, and the agent reads PR content, so Qodex treats every probe as untrusted.`}),`\n`,_jsxs(_components.p,{children:[`Qodex refuses probes that target unsafe or private destinations: HTTP, `,_jsx(_components.code,{children:`localhost`}),`, private or reserved IP ranges, IPv6 literals, and obfuscated IP encodings are all rejected before any request is made.`]}),`\n`,_jsx(Heading,{level:`2`,id:`per-repo-allowlist`,children:`Per-repo allowlist`}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`.qodex.yaml`}),` can restrict preview probes to known hosts.`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`4`,language:`yaml`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`yaml`,children:_jsxs(_components.code,{language:`yaml`,numberOfLines:`4`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`probes`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:` preview_host_allowlist`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`:`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"*.vercel.app\"`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"preview.acme-staging.com\"`})]}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Patterns may start with `,_jsx(_components.code,{children:`*.`}),` to allow a domain and its subdomains. A `,_jsx(_components.code,{children:`null`}),` allowlist, the default, is permissive, but the global SSRF guard still applies.`]}),`\n`,_jsx(_components.p,{children:`When the allowlist is set and the discovered host does not match, the review proceeds without probes and the walkthrough explains that the preview deployment was skipped.`}),`\n`,_jsx(Heading,{level:`2`,id:`probe-execution`,children:`Probe execution`}),`\n`,_jsx(_components.p,{children:`Once a probe URL is resolved and origin-checked:`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Headers are Qodex-controlled only. Any headers suggested by the agent are dropped.`}),`\n`,_jsx(_components.li,{children:`Body is empty. Any body suggested by the agent is ignored.`}),`\n`,_jsx(_components.li,{children:`Redirects are not followed.`}),`\n`,_jsx(_components.li,{children:`Timeout is 8 seconds.`}),`\n`,_jsx(_components.li,{children:`Response body is captured up to 8 KB and then truncated.`}),`\n`]}),`\n`,_jsx(_components.p,{children:`The response body is escaped before it is embedded in the inline comment so hostile payloads cannot break out of the markdown fence.`}),`\n`,_jsx(Heading,{level:`2`,id:`probe-results`,children:`Probe results`}),`\n`,_jsx(_components.p,{children:`Each finding gets one probe status.`}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Status`}),_jsx(_components.th,{children:`When`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`verified`})}),_jsx(_components.td,{children:`Probe response matched the expected signal.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`unverified`})}),_jsx(_components.td,{children:`Probe ran but the response did not match. The finding still posts, but certainty is lower.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`failed`})}),_jsx(_components.td,{children:`Probe could not reach the preview because of DNS, timeout, refused origin, or malformed URL.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`skipped`})}),_jsx(_components.td,{children:`No probe was emitted, no preview was discoverable, or the allowlist blocked the discovered host.`})]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`inline-render`,children:`Inline render`}),`\n`,_jsx(_components.p,{children:`When a probe runs, the inline comment includes a probe badge and a collapsible “Probe evidence” block.`}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`21`,language:`markdown`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`markdown`,children:_jsxs(_components.code,{language:`markdown`,numberOfLines:`21`,children:[_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Critical `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-light-font-weight\":`bold`,\"--shiki-dark\":`#569CD6`,\"--shiki-dark-font-weight\":`bold`},children:`**Missing auth on profile endpoint**`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - `}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-light-font-style\":`italic`,\"--shiki-dark\":`#D4D4D4`,\"--shiki-dark-font-style\":`italic`},children:`_security_`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` - Verified`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`The endpoint reads the ID from the URL and queries the row without`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`checking the session user.`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:\"```suggestion\"})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`if (id !== req.session.userId) return res.status(403).end();`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:\"```\"})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`\u003c`}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`details`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`>\u003c`}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`summary`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`>`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`Probe evidence`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`\u003c/`}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`summary`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`>`})]}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:\"**Request** `GET https://pr-42.preview.acme.app/api/profile/9999`\"})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:\"**Response** `200 OK` in 142ms\"})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`~~~`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{\"id\":\"9999\",\"email\":\"victim@example.com\"}`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`~~~`})}),`\n`,_jsx(_components.span,{className:`line`}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`**Verdict** response status 200 matched expected 200`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`\u003c/`}),_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#569CD6`},children:`details`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#808080`},children:`>`})]}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`on-the-roadmap`,children:`On the roadmap`}),`\n`,_jsx(Tip,{children:_jsxs(_components.p,{children:[`Opt-in non-GET probes (`,_jsx(_components.code,{children:`POST`}),`, `,_jsx(_components.code,{children:`PUT`}),`, `,_jsx(_components.code,{children:`PATCH`}),`, `,_jsx(_components.code,{children:`DELETE`}),`) are on the roadmap, gated by `,_jsx(_components.code,{children:`.qodex.yaml`}),`’s `,_jsx(_components.code,{children:`probes.allow_non_get: true`}),`. They are useful for verifying state-mutating bugs on preview environments you are willing to mutate.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`related`,children:`Related`}),`\n`,_jsxs(CardGroup,{cols:2,children:[_jsx(Card,{title:`Inline findings`,icon:`message-square-text`,href:`/pr-review-inline-findings`}),_jsx(Card,{title:`.qodex.yaml reference`,icon:`file-code`,href:`/qodex-yaml-reference`}),_jsx(Card,{title:`Check Run and merge gating`,icon:`shield-check`,href:`/pr-review-check-run`}),_jsx(Card,{title:`Troubleshooting`,icon:`life-buoy`,href:`/pr-review-troubleshooting`})]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Verification probes","description":"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.","icon":"zap","href":"/pr-review-verification-probes"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Verification probes","slug":"verification-probes","depth":1,"children":[]},{"title":"How probes work","slug":"how-probes-work","depth":2,"children":[]},{"title":"Probe spec","slug":"probe-spec","depth":2,"children":[]},{"title":"Method","slug":"method","depth":2,"children":[]},{"title":"Path","slug":"path","depth":2,"children":[]},{"title":"Expected signal","slug":"expected-signal","depth":2,"children":[]},{"title":"Preview URL discovery","slug":"preview-url-discovery","depth":2,"children":[]},{"title":"Security guards","slug":"security-guards","depth":2,"children":[]},{"title":"Per-repo allowlist","slug":"per-repo-allowlist","depth":2,"children":[]},{"title":"Probe execution","slug":"probe-execution","depth":2,"children":[]},{"title":"Probe results","slug":"probe-results","depth":2,"children":[]},{"title":"Inline render","slug":"inline-render","depth":2,"children":[]},{"title":"On the roadmap","slug":"on-the-roadmap","depth":2,"children":[]},{"title":"Related","slug":"related","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"qodexai","siteName":"Qodex","title":"Verification probes - Qodex","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-08-16T16:01:47.652Z","originalFileLocation":"pr-review-verification-probes.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Qodex\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Verification probes - Qodex\">\u003cmeta data-mint-head property=\"og:description\" content=\"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Qodex\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://qodex.ai/docs/pr-review-verification-probes\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://qodexai.mintlify.site/docs/_mintlify/api/og/v2?division=PR+review\u0026amp;title=Verification+probes\u0026amp;description=How+Qodex+safely+checks+whether+a+PR+finding+is+reproducible+on+a+preview+deployment.+Learn+what+runs%2C+what+is+safe%2C+and+what+can+fail.\u0026amp;theme=9930d66f3eecbeefb92e0bc0\u0026amp;signature=ae184a4b49a41b3d78b97c9ab4d9a4596c59e242d2ea2ac8e78fd442b461adb3\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Verification probes - Qodex\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://qodexai.mintlify.site/docs/_mintlify/api/og/v2?division=PR+review\u0026amp;title=Verification+probes\u0026amp;description=How+Qodex+safely+checks+whether+a+PR+finding+is+reproducible+on+a+preview+deployment.+Learn+what+runs%2C+what+is+safe%2C+and+what+can+fail.\u0026amp;theme=9930d66f3eecbeefb92e0bc0\u0026amp;signature=ae184a4b49a41b3d78b97c9ab4d9a4596c59e242d2ea2ac8e78fd442b461adb3\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/docs/mintlify-assets/_mintlify/favicons/qodexai/CvWudqS8dDlB66QO/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Qodex\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#3B82F6\">\u003clink data-mint-head rel=\"canonical\" href=\"https://qodex.ai/docs/pr-review-verification-probes\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/docs/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/docs/pr-review-verification-probes.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://qodex.ai/#organization\",\"name\":\"Qodex\",\"url\":\"https://qodex.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/qodexai/P6C0FF6uddKFEAsX/logo-coral.svg?fit=max\u0026auto=format\u0026n=P6C0FF6uddKFEAsX\u0026q=85\u0026s=4b3af197a898df33ff25e878e4209af8\"}},{\"@type\":\"WebSite\",\"@id\":\"https://qodex.ai/docs#website\",\"name\":\"Qodex\",\"url\":\"https://qodex.ai/docs\",\"publisher\":{\"@id\":\"https://qodex.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://qodex.ai/docs/pr-review-verification-probes#webpage\",\"url\":\"https://qodex.ai/docs/pr-review-verification-probes\",\"name\":\"Verification probes\",\"description\":\"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\",\"dateModified\":\"2026-08-16T16:01:47.652Z\",\"isPartOf\":{\"@id\":\"https://qodex.ai/docs#website\"},\"breadcrumb\":{\"@id\":\"https://qodex.ai/docs/pr-review-verification-probes#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://qodex.ai/docs/pr-review-verification-probes#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"PR review\",\"item\":\"https://qodex.ai/docs/pr-review\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Verification probes\",\"item\":\"https://qodex.ai/docs/pr-review-verification-probes\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://qodex.ai/docs/pr-review-verification-probes#article\",\"headline\":\"Verification probes\",\"name\":\"Verification probes\",\"description\":\"How Qodex safely checks whether a PR finding is reproducible on a preview deployment. Learn what runs, what is safe, and what can fail.\",\"url\":\"https://qodex.ai/docs/pr-review-verification-probes\",\"mainEntityOfPage\":{\"@id\":\"https://qodex.ai/docs/pr-review-verification-probes#webpage\"},\"dateModified\":\"2026-08-16T16:01:47.652Z\",\"publisher\":{\"@id\":\"https://qodex.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://qodex.ai/docs#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Qodex\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Qodex","nav":{"global":{"anchors":[{"anchor":"Demo Call with CEO","icon":"phone","href":"https://cal.com/siddhant-mohan/45mins?duration=45\u0026overlayCalendar=true"},{"anchor":"Blog","icon":"newspaper","href":"https://qodex.ai/blog"},{"anchor":"Discord","icon":"message-circle","href":"https://discord.gg/h2tP328g"},{"anchor":"Changelog","icon":"scroll-text","href":"/changelog"}]},"tabs":[{"tab":"Documentation","groups":[{"group":"Get started","icon":"rocket","pages":[{"title":"Introduction","icon":"sparkles","href":"/introduction"},{"title":"Quickstart","icon":"play","href":"/quickstart"},{"title":"How Qodex works","icon":"workflow","href":"/how-qodex-works"},{"title":"Operating modes","icon":"layers","href":"/operating-modes"},{"title":"How Qodex compares","icon":"scale","href":"/comparisons"}]},{"group":"Concepts","icon":"circuit-board","pages":[{"title":"How scenarios work","sidebarTitle":"Scenarios","icon":"list-checks","href":"/concepts-scenarios"},{"title":"Scripts","icon":"code","href":"/concepts-scripts"},{"title":"How findings work","sidebarTitle":"Findings","icon":"bug","href":"/concepts-findings"},{"title":"How skills work","sidebarTitle":"Skills","icon":"puzzle","href":"/concepts-skills"},{"title":"How memory works","sidebarTitle":"Memory","icon":"brain","href":"/concepts-memory"},{"title":"Projects","icon":"folder","href":"/concepts-projects"}]},{"group":"PR review","icon":"git-pull-request","pages":[{"title":"PR review","icon":"git-pull-request","href":"/pr-review"},{"title":"How a review fires","icon":"webhook","href":"/pr-review-how-it-fires"},{"title":"Walkthrough anatomy","icon":"message-square","href":"/pr-review-walkthrough-anatomy"},{"title":"Inline findings","description":"How Qodex turns PR review findings into line comments with severity, confidence, evidence, and suggestions.","icon":"message-square-text","href":"/pr-review-inline-findings"},{"title":"Verification probes","icon":"zap","href":"/pr-review-verification-probes"},{"title":"Check Run and merge gating","description":"How Qodex reports PR review status in GitHub and how to opt in to merge blocking. Learn how statuses map to merge decisions.","icon":"shield-check","href":"/pr-review-check-run"},{"title":"Slash commands","icon":"terminal","href":"/pr-review-slash-commands"},{"group":"Setting up","icon":"settings","pages":[{"title":"Install the GitHub App","icon":"github","href":"/pr-review-install-github-app"},{"title":"Connect a repo","icon":"link","href":"/pr-review-connect-repo"},{"title":"Disconnect a GitHub account","icon":"unplug","href":"/pr-review-disconnect"},{"title":"Multi-project routing","icon":"git-fork","href":"/pr-review-multi-project-routing"}]},{"title":"Limits and caveats","icon":"ruler","href":"/pr-review-limits-caveats"},{"title":"Troubleshooting PR review","icon":"life-buoy","href":"/pr-review-troubleshooting"}]},{"group":"API testing","icon":"code","pages":[{"title":"API testing","icon":"plug","href":"/api-testing"},{"title":"Import an OpenAPI spec","icon":"file-code","href":"/api-testing-openapi-import"},{"title":"Import a Postman collection","icon":"folder-input","href":"/api-testing-postman-import"},{"title":"API test scenarios","sidebarTitle":"Scenarios","icon":"list-checks","href":"/api-testing-scenarios"},{"title":"Chaining and postscripts","icon":"link","href":"/api-testing-chaining"},{"title":"API Playground","icon":"terminal","href":"/api-testing-playground"},{"title":"Auth profiles","icon":"key","href":"/api-testing-auth-profiles"},{"title":"Auto-verification on save","icon":"circle-check","href":"/api-testing-auto-verification"},{"title":"Request data generation","icon":"sparkles","href":"/api-testing-request-data-generation"},{"title":"Test rules in plain English","icon":"check-check","href":"/api-testing-test-rules"},{"title":"Endpoint catalog","icon":"list","href":"/api-testing-endpoint-catalog"},{"title":"API governance","icon":"map","href":"/api-testing-governance"}]},{"group":"UI testing","icon":"monitor","pages":[{"title":"UI testing","icon":"monitor","href":"/ui-testing"},{"title":"Crawling and the Pages catalog","icon":"map","href":"/ui-testing-crawling"},{"title":"Intent-driven UI scenarios","icon":"wand-sparkles","href":"/ui-testing-intent-driven-scenarios"},{"title":"Replay cache and self-healing","icon":"recycle","href":"/ui-testing-replay-cache"},{"title":"Per-step artifacts","icon":"camera","href":"/ui-testing-artifacts"}]},{"group":"Security testing","icon":"shield","pages":[{"title":"Security testing","icon":"shield-check","href":"/security-testing"},{"title":"OWASP API Top 10 in Qodex","icon":"list-ordered","href":"/security-testing-owasp-api-top-10"},{"title":"Security scenarios","icon":"shield-half","href":"/security-testing-scenarios"},{"title":"Inverted semantics","icon":"arrows-up-down","href":"/security-testing-inverted-semantics"},{"title":"Sensitive endpoints","icon":"lock","href":"/security-testing-sensitive-endpoints"}]},{"group":"Load testing","icon":"gauge","pages":[{"title":"Load testing","icon":"gauge","href":"/load-testing"}]},{"group":"Run tests","icon":"play","pages":[{"title":"Run tests","icon":"play","href":"/run-tests"},{"title":"Run tests from the web app","icon":"mouse-pointer-click","href":"/run-tests-from-web-app"},{"title":"Run tests on a schedule","icon":"calendar-clock","href":"/run-tests-on-schedule"},{"title":"Run tests via webhook","icon":"webhook","href":"/run-tests-via-webhook"},{"title":"Run a single scenario","icon":"play","href":"/run-tests-single-scenario"},{"title":"Run tag-filtered scenarios","icon":"tag","href":"/run-tests-tag-filtered"},{"title":"Test groups and folders","icon":"folder","href":"/run-tests-test-groups-and-folders"},{"title":"Run the full suite","icon":"list-checks","href":"/run-tests-full-suite"},{"title":"Re-run failed tests","icon":"rotate-ccw","href":"/run-tests-rerun-failed"},{"group":"In CI","icon":"git-branch","pages":[{"title":"Run tests in CI","icon":"git-branch","href":"/run-tests-in-ci"},{"title":"Run tests in GitHub Actions","icon":"github","href":"/run-tests-ci-github-actions"},{"title":"Run tests in GitLab CI","icon":"gitlab","href":"/run-tests-ci-gitlab"},{"title":"Run tests in CircleCI","icon":"circle","href":"/run-tests-ci-circleci"},{"title":"Run tests in Buildkite","icon":"hammer","href":"/run-tests-ci-buildkite"},{"title":"Run tests in Jenkins","icon":"server","href":"/run-tests-ci-jenkins"},{"title":"Run tests with a generic shell","icon":"terminal","href":"/run-tests-ci-generic-shell"}]}]},{"group":"Autopilot","icon":"bot","pages":[{"title":"Autopilot","icon":"bot","href":"/autopilot"},{"title":"Missions and agents","icon":"target","href":"/missions"}]},{"group":"Findings","icon":"bug","pages":[{"title":"Findings","icon":"bug","href":"/findings"},{"title":"Severity model","icon":"signal","href":"/findings-severity-model"},{"title":"Failure classification","icon":"filter","href":"/findings-failure-classification"},{"title":"Triage workflow","icon":"list-checks","href":"/findings-triage-workflow"}]},{"group":"Memory","icon":"brain","pages":[{"title":"Project memory and reusable context","icon":"brain","href":"/memory"},{"title":"business.md","icon":"pencil","href":"/memory-project-md"},{"title":"Editing and curating memory","icon":"pencil","href":"/memory-editing-and-curating"}]},{"group":"Skills","icon":"blocks","pages":[{"title":"Agent skills and custom workflows","icon":"puzzle","href":"/skills"},{"title":"The .skill.md format","icon":"file-code","href":"/skills-format"},{"title":"Built-in skills","icon":"layers","href":"/skills-built-in"},{"title":"Author a skill","icon":"file-plus","href":"/skills-authoring"},{"title":"Distribute a skill","icon":"share-2","href":"/skills-distributing"}]},{"group":"Integrations","icon":"plug","pages":[{"title":"Integrations","icon":"plug","href":"/integrations"},{"title":"GitHub integration for PR reviews","icon":"git-branch","href":"/integrations-github"},{"title":"Syncing Tests to GitHub","href":"/syncing-tests-to-github"},{"title":"GitLab","icon":"gitlab","href":"/integrations-gitlab"},{"title":"MCP","icon":"blocks","href":"/integrations-mcp"},{"title":"Slack integration for test alerts","icon":"message-square","href":"/integrations-slack"},{"title":"Generic webhook","icon":"webhook","href":"/integrations-generic-webhook"},{"title":"Bring your own key","icon":"key","href":"/integrations-byok"}]},{"group":"Self-hosted","icon":"server","pages":[{"title":"Self-hosted","icon":"server","href":"/self-hosted"},{"title":"Single-container deploy","icon":"box","href":"/self-hosted-single-container"},{"title":"Docker Compose","icon":"layers","href":"/self-hosted-docker-compose"},{"title":"AWS Terraform reference","icon":"cloud","href":"/self-hosted-aws-terraform"},{"title":"Environment variables","icon":"settings","href":"/self-hosted-environment-variables"},{"title":"Storage backends","icon":"hard-drive","href":"/self-hosted-storage-backends"},{"title":"Secret management","icon":"key","href":"/self-hosted-secret-management"}]},{"group":"Account","icon":"user","pages":[{"title":"Workspaces and projects","icon":"folder","href":"/account-workspaces-and-projects"},{"title":"Members and roles","icon":"users","href":"/account-members-and-roles"},{"title":"Plans and pricing","icon":"credit-card","href":"/account-plans-and-pricing"},{"title":"Usage and cost caps","icon":"gauge","href":"/account-usage-and-cost-caps"},{"title":"Security and data handling","icon":"lock","href":"/security-and-data-handling"}]}]},{"tab":"Reference","pages":[{"title":"Reference","href":"/qodex-yaml-overview"}]},{"tab":"OpenQodex","pages":[{"title":"OpenQodex","href":"/openqodex"}]},{"tab":"Changelog","pages":[{"title":"Changelog","href":"/changelog"}]}]},"basePath":"","title":"Verification probes - Qodex","buildId":"dpl_ATnXPMW25DMPUr9VBw6kL6MfBt6T","deploymentId":"dpl_ATnXPMW25DMPUr9VBw6kL6MfBt6T","dynamics":[],"chunks":[]}