Viešas API skenavimams, stebėjimui, leidimų koreliacijai ir portfelio darbo eigoms.
Vienas tiesos šaltinis, tipizuoti užklausų ir atsakymų modeliai, atsisiunčiamas OpenAPI ir sugeneruotas TypeScript klientas agentūros lygio integracijoms.
Autentifikavimas
Naudokite `Authorization: Bearer <api_key>`. API raktai yra apriboti ir valdomi iš paskyros nustatymų.
Greičio apribojimai
Numatytasis viešo API biudžetas yra `100 užkl./min` vienam raktui, griežtesni galinio taško apribojimai dokumentuoti specifikacijoje.
Webhook modelis
Konfigūruokite siunčiamus pranešimus `scan_complete` ir `regression_alert` įvykiams, pasirašytus žemiau dokumentuotomis HMAC antraštėmis.
Pirmas skenavimas
cURLcurl -fsS -X POST "$BASE_URL/api/v1/scan" \
-H "Authorization: Bearer $GDPRMONITOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"viewport": "desktop"
}'TypeScript klientas
Sugeneruotas SDKimport { Configuration, ScansApi } from "@gdprmonitor/public-api-client";
const client = new ScansApi(
new Configuration({
basePath: process.env.GDPRMONITOR_BASE_URL,
accessToken: process.env.GDPRMONITOR_API_KEY,
})
);
const queued = await client.createScan({
scanCreateRequest: {
url: "https://example.com",
viewport: "desktop",
},
});
const result = await client.getScanStatus({ scanId: queued.id });Nuoroda
Tiesioginė API dokumentacija
Žemiau esanti nuoroda atvaizduota tiesiogiai iš tos pačios `docs/openapi.yaml` sutarties, naudojamos SDK generavimui ir CI nukrypimų tikrinimui.
Rendered contract
This view is rendered directly from `docs/openapi.yaml` without runtime style injection, so it remains compatible with the app CSP.
openapi: 3.1.0
info:
title: GDPR Privacy Monitor Public API
version: 1.0.0
summary: Contract-first public API for customer integrations.
description: |-
GDPR Privacy Monitor exposes a scoped bearer API for scans, monitoring, release correlation, portfolio reporting, and webhook configuration.
Design constraints:
- This specification covers only the customer-facing `api/v1` surface.
- Internal and admin endpoints are intentionally excluded.
- Authentication uses scoped bearer API keys, not OAuth2 tokens.
servers:
- url: https://gdprprivacymonitor.eu
description: Production public API base URL. Override `basePath` in the SDK for self-hosted or staging deployments.
tags:
- name: Scans
- name: Monitoring
- name: Release Markers
- name: Webhooks
- name: Portfolio
- name: Status
- name: Disputes
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: API Key
description: Scoped bearer API key managed in Account Settings.
parameters:
ScanID:
in: path
name: scanId
required: true
schema:
type: string
format: uuid
description: Scan UUID.
MonitorID:
in: path
name: monitorId
required: true
schema:
type: string
format: uuid
description: Monitor UUID.
MarkerID:
in: path
name: markerId
required: true
schema:
type: string
format: uuid
description: Release marker UUID.
responses:
BadRequest:
description: Invalid request payload, query, or path parameter.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
Unauthorized:
description: Missing or invalid API key.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
Forbidden:
description: Scope or plan restriction prevented this action.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
NotFound:
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
Conflict:
description: Resource state does not allow the requested action.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
Unprocessable:
description: Semantically invalid resource state or relationship.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
TooManyRequests:
description: Rate limit exceeded.
headers:
Retry-After:
schema:
type: string
description: Seconds until the next allowed request.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
InternalError:
description: Internal server error.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
ServiceUnavailable:
description: Temporary service or rate limit backend issue.
headers:
Retry-After:
schema:
type: string
description: Suggested retry window in seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
examples:
WebhookScanComplete:
summary: scan_complete webhook delivery
value:
event_type: scan_complete
sent_at: '2026-03-03T12:00:00.000Z'
scan:
id: 11111111-1111-4111-8111-111111111111
url: https://example.com
monitor_id: 22222222-2222-4222-8222-222222222222
risk_score: 42
risk_level: Medium
regression_message: null
regression_details: {}
WebhookRegressionAlert:
summary: regression_alert webhook delivery
value:
event_type: regression_alert
sent_at: '2026-03-07T09:30:00.000Z'
scan:
id: 33333333-3333-4333-8333-333333333333
url: https://shop.example.com
monitor_id: 44444444-4444-4444-8444-444444444444
risk_score: 71
risk_level: High
regression_message: New trackers load before consent.
regression_details:
severity: critical
finding_types:
- new_tracker_detected
- risk_score_regression
schemas:
APIError:
type: object
properties:
error:
type: string
retry_after:
type: integer
minimum: 1
required:
- error
additionalProperties: false
OKResponse:
type: object
properties:
ok:
type: boolean
required:
- ok
additionalProperties: false
ScanCreateRequest:
type: object
properties:
url:
type: string
format: uri
viewport:
type: string
enum: [desktop, mobile]
default: desktop
required:
- url
additionalProperties: false
ScanSubmissionResponse:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [queued]
required:
- id
- status
additionalProperties: false
ButtonMetrics:
type: object
properties:
tag_name:
type: string
background_color:
type: string
font_size_px:
type: number
color:
type: string
opacity:
type: number
display:
type: string
visibility:
type: string
width_px:
type: number
height_px:
type: number
required:
- tag_name
- background_color
- font_size_px
- color
- opacity
- display
- visibility
- width_px
- height_px
additionalProperties: false
ProminenceSignal:
type: object
properties:
name:
type: string
description:
type: string
is_dark_pattern:
type: boolean
weight:
type: number
required:
- name
- description
- is_dark_pattern
- weight
additionalProperties: false
ProminenceOutput:
type: object
properties:
suppressed:
type: boolean
confidence:
type: number
cannot_determine:
type: boolean
reason:
type: string
accept_metrics:
$ref: '#/components/schemas/ButtonMetrics'
reject_metrics:
$ref: '#/components/schemas/ButtonMetrics'
signals:
type: array
items:
$ref: '#/components/schemas/ProminenceSignal'
required:
- suppressed
- confidence
- cannot_determine
- accept_metrics
- reject_metrics
additionalProperties: false
BannerOutput:
type: object
properties:
detected:
type: boolean
cmp:
type: string
cmp_level:
type: string
detection_mode:
type: string
screenshot_mode:
type: string
reject_available:
type: boolean
reject_below_fold:
type: boolean
cookie_wall:
type: boolean
prominence:
$ref: '#/components/schemas/ProminenceOutput'
vendor_list:
$ref: '#/components/schemas/VendorListOutput'
first_layer:
$ref: '#/components/schemas/FirstLayerOutput'
required:
- detected
additionalProperties: false
CookieOutput:
type: object
properties:
name:
type: string
domain:
type: string
category:
type: string
service:
type: string
required:
- name
- domain
- category
additionalProperties: false
TrackerOutput:
type: object
properties:
service:
type: string
category:
type: string
url:
type: string
info_only:
type: boolean
required:
- service
- category
- url
additionalProperties: false
CookieDiffOutput:
type: object
properties:
pre_consent:
type: array
items:
$ref: '#/components/schemas/CookieOutput'
post_consent_only:
type: array
items:
$ref: '#/components/schemas/CookieOutput'
persistent:
type: array
items:
$ref: '#/components/schemas/CookieOutput'
required:
- pre_consent
- post_consent_only
- persistent
additionalProperties: false
TrackerDiffOutput:
type: object
properties:
pre_consent:
type: array
items:
$ref: '#/components/schemas/TrackerOutput'
post_consent_only:
type: array
items:
$ref: '#/components/schemas/TrackerOutput'
required:
- pre_consent
- post_consent_only
additionalProperties: false
ThirdPartyDomainOutput:
type: object
properties:
domain:
type: string
request_count:
type: integer
required:
- domain
- request_count
additionalProperties: false
ThirdPartyDomainsOutput:
type: object
properties:
count:
type: integer
top:
type: array
items:
$ref: '#/components/schemas/ThirdPartyDomainOutput'
required:
- count
- top
additionalProperties: false
PreferenceCategoryOutput:
type: object
properties:
category:
type: string
label:
type: string
enabled_by_default:
type: boolean
disabled:
type: boolean
required:
- category
- label
- enabled_by_default
additionalProperties: false
PreferenceCategoryScanOutput:
type: object
properties:
category:
type: string
label:
type: string
cookies:
type: array
items:
$ref: '#/components/schemas/CookieOutput'
trackers:
type: array
items:
$ref: '#/components/schemas/TrackerOutput'
removal_verified:
type: boolean
warnings:
type: array
items:
type: string
required:
- category
- label
additionalProperties: false
PreferenceCenterOutput:
type: object
properties:
detected:
type: boolean
categories:
type: array
items:
$ref: '#/components/schemas/PreferenceCategoryOutput'
category_scans:
type: array
items:
$ref: '#/components/schemas/PreferenceCategoryScanOutput'
withdrawal_reachability:
$ref: '#/components/schemas/WithdrawalReachabilityOutput'
warnings:
type: array
items:
type: string
required:
- detected
additionalProperties: false
ConsentProofOutput:
type: object
properties:
phase:
type: string
context:
type: string
api:
type: string
available:
type: boolean
tc_string_present:
type: boolean
gdpr_applies:
type:
- boolean
- 'null'
purpose_consents_granted:
type: integer
vendor_consents_granted:
type: integer
gpp_string_present:
type: boolean
capture_error:
type: string
required:
- phase
- context
- available
additionalProperties: false
VendorListOutput:
type: object
properties:
tcf_detected:
type: boolean
found:
type: boolean
layer:
type: string
text_snippet:
type: string
href:
type: string
visible_in_dom:
type: boolean
required:
- tcf_detected
- found
additionalProperties: false
FirstLayerOutput:
type: object
properties:
banner_text:
type: string
policy_link_found:
type: boolean
policy_link_href:
type: string
policy_link_text:
type: string
purpose_keywords:
type: array
items:
type: string
purposes_found:
type: boolean
controller_found:
type: boolean
confidence:
type: string
required:
- policy_link_found
- purposes_found
- controller_found
additionalProperties: false
WithdrawalReachabilityOutput:
type: object
properties:
evaluated:
type: boolean
reachable:
type: boolean
method:
type: string
api_only:
type: boolean
launcher_text:
type: string
withdrawal_clicks:
type: integer
accept_clicks:
type: integer
required:
- evaluated
- reachable
additionalProperties: false
RejectFlowOutput:
type: object
properties:
executed:
type: boolean
reject_worked:
type: boolean
inconclusive:
type: boolean
persistent_cookies:
type: array
items:
$ref: '#/components/schemas/CookieOutput'
persistent_trackers:
type: array
items:
$ref: '#/components/schemas/TrackerOutput'
warnings:
type: array
items:
type: string
required:
- executed
- reject_worked
additionalProperties: false
AccessibilityCheckOutput:
type: object
properties:
name:
type: string
wcag_criterion:
type: string
passed:
type: boolean
severity:
type: string
description:
type: string
details:
type: string
recommendation:
type: string
required:
- name
- wcag_criterion
- passed
- severity
- description
additionalProperties: false
AccessibilityOutput:
type: object
properties:
score:
type: number
level:
type: string
checks:
oneOf:
- type: array
items:
$ref: '#/components/schemas/AccessibilityCheckOutput'
- type: 'null'
warnings:
type: array
items:
type: string
required:
- score
- level
additionalProperties: false
ConsentModeCheckOutput:
type: object
properties:
parameter:
type: string
declared_state:
type: string
phase:
type: string
passed:
type: boolean
violations:
type: array
items:
type: string
required:
- parameter
- declared_state
- phase
- passed
additionalProperties: false
ConsentModeMismatchOutput:
type: object
properties:
parameter:
type: string
declared:
type: string
observed:
type: string
phase:
type: string
severity:
type: string
required:
- parameter
- declared
- observed
- phase
- severity
additionalProperties: false
ConsentMap:
type: object
additionalProperties:
type: string
ConsentModeOutput:
type: object
properties:
detected:
type: boolean
mode:
type: string
google_signals_present:
type: boolean
default_consent:
$ref: '#/components/schemas/ConsentMap'
update_consent:
$ref: '#/components/schemas/ConsentMap'
update_timing_ms:
type: integer
update_timing_basis:
type: string
default_conflicts:
type: array
items:
type: string
update_conflicts:
type: array
items:
type: string
server_side_gtm_detected:
type: boolean
cannot_determine:
type: boolean
cannot_determine_reason:
type: string
extraction_error:
type: string
checks:
type: array
items:
$ref: '#/components/schemas/ConsentModeCheckOutput'
mismatches:
type: array
items:
$ref: '#/components/schemas/ConsentModeMismatchOutput'
required:
- detected
- mode
- google_signals_present
additionalProperties: false
FingerprintSignalOutput:
type: object
properties:
api:
type: string
detail:
type: string
count:
type: integer
phase:
type: string
stack:
type: string
required:
- api
- count
- phase
additionalProperties: false
FingerprintingOutput:
type: object
properties:
pre_consent_score:
type: number
pre_consent_likelihood:
type: string
pre_consent_signals:
type: array
items:
$ref: '#/components/schemas/FingerprintSignalOutput'
post_consent_signals:
type: array
items:
$ref: '#/components/schemas/FingerprintSignalOutput'
suspected_scripts:
type: array
items:
type: string
required:
- pre_consent_score
- pre_consent_likelihood
additionalProperties: false
PolicyGapOutput:
type: object
properties:
type:
type: string
severity:
type: string
explanation:
type: string
policy_claim:
type: string
scan_evidence:
type: string
recommendation:
type: string
required:
- type
- severity
- explanation
additionalProperties: false
PolicyLLMOutput:
type: object
properties:
provider:
type: string
model:
type: string
input_tokens:
type: integer
output_tokens:
type: integer
estimated_cost_usd:
type: number
budget_usd:
type: number
skipped_reason:
type: string
required:
- provider
- model
- input_tokens
- output_tokens
- estimated_cost_usd
additionalProperties: false
PolicyAnalysisOutput:
type: object
properties:
enabled:
type: boolean
policy_url:
type: string
detected_by:
type: string
fetch_status:
type: string
language:
type: string
text_chars:
type: integer
gaps:
type: array
items:
$ref: '#/components/schemas/PolicyGapOutput'
summary:
type: string
warnings:
type: array
items:
type: string
llm:
$ref: '#/components/schemas/PolicyLLMOutput'
required:
- enabled
additionalProperties: false
FixGuideOutput:
type: object
properties:
title:
type: string
steps:
type: array
items:
type: string
docs_url:
type: string
format: uri
last_verified:
type: string
required:
- title
- steps
- last_verified
additionalProperties: false
FindingOutput:
type: object
properties:
type:
type: string
severity:
type: string
description:
type: string
points:
type: integer
count:
type: integer
fix_guide:
$ref: '#/components/schemas/FixGuideOutput'
required:
- type
- severity
- description
- points
additionalProperties: false
DataFlowCountryOutput:
type: object
properties:
organization_count:
type: integer
organizations:
type: array
items:
type: string
adequacy_status:
type: string
adequacy_note:
type: string
required:
- organization_count
- organizations
- adequacy_status
additionalProperties: false
DataFlowRowOutput:
type: object
properties:
domain:
type: string
organization:
type: string
country:
type: string
country_name:
type: string
adequacy_status:
type: string
request_count:
type: integer
pre_consent:
type: boolean
required:
- domain
- organization
- country
- country_name
- adequacy_status
- request_count
- pre_consent
additionalProperties: false
DataFlowCountriesMap:
type: object
additionalProperties:
$ref: '#/components/schemas/DataFlowCountryOutput'
DataFlowOutput:
type: object
properties:
total_third_party_domains:
type: integer
total_organizations:
type: integer
countries:
$ref: '#/components/schemas/DataFlowCountriesMap'
flows:
type: array
items:
$ref: '#/components/schemas/DataFlowRowOutput'
unmapped_domains:
type: array
items:
type: string
required:
- total_third_party_domains
- total_organizations
- countries
- flows
additionalProperties: false
ScanReliabilityOutput:
type: object
properties:
level:
type: string
enum: [high, degraded]
request_log_truncated:
type: boolean
dropped_requests:
type: integer
har_excluded:
type: boolean
pdf_failed:
type: boolean
required:
- level
additionalProperties: false
MetadataOutput:
type: object
properties:
scanner_version:
type: string
wait_strategy:
type: string
viewport:
type: string
selector_version:
type: string
required:
- scanner_version
- wait_strategy
- viewport
additionalProperties: false
EnforcementNotableCaseOutput:
type: object
properties:
company:
type: string
fine:
type: string
date:
type: string
required:
- company
- fine
- date
additionalProperties: false
EnforcementFindingContextOutput:
type: object
properties:
finding_type:
type: string
enforcement_level:
type: string
historical_actions:
type: integer
historical_fines_total:
type: string
notable_case:
$ref: '#/components/schemas/EnforcementNotableCaseOutput'
context_text:
type: string
required:
- finding_type
- enforcement_level
- historical_actions
- historical_fines_total
- context_text
additionalProperties: false
EnforcementDPAOutput:
type: object
properties:
code:
type: string
name:
type: string
country:
type: string
summary:
type: string
required:
- code
- name
- country
- summary
additionalProperties: false
EnforcementContextOutput:
type: object
properties:
jurisdiction:
type: string
dpa:
oneOf:
- $ref: '#/components/schemas/EnforcementDPAOutput'
- type: 'null'
findings:
type: array
items:
$ref: '#/components/schemas/EnforcementFindingContextOutput'
overall_risk_context:
type: string
disclaimer:
type: string
db_version:
type: string
required:
- jurisdiction
- dpa
- findings
- overall_risk_context
- disclaimer
- db_version
additionalProperties: false
ScanResultOutput:
type: object
properties:
url:
type: string
format: uri
final_url:
type: string
format: uri
scan_duration_ms:
type: integer
risk_score:
type: integer
risk_level:
type: string
banner:
$ref: '#/components/schemas/BannerOutput'
preference_center:
$ref: '#/components/schemas/PreferenceCenterOutput'
reject_flow:
$ref: '#/components/schemas/RejectFlowOutput'
accessibility:
$ref: '#/components/schemas/AccessibilityOutput'
consent_mode:
$ref: '#/components/schemas/ConsentModeOutput'
fingerprinting:
$ref: '#/components/schemas/FingerprintingOutput'
policy_analysis:
$ref: '#/components/schemas/PolicyAnalysisOutput'
findings:
type: array
items:
$ref: '#/components/schemas/FindingOutput'
cookies:
$ref: '#/components/schemas/CookieDiffOutput'
trackers:
$ref: '#/components/schemas/TrackerDiffOutput'
third_party_domains:
$ref: '#/components/schemas/ThirdPartyDomainsOutput'
data_flows:
$ref: '#/components/schemas/DataFlowOutput'
consent_proof:
type: array
items:
$ref: '#/components/schemas/ConsentProofOutput'
enforcement_context:
$ref: '#/components/schemas/EnforcementContextOutput'
warnings:
type: array
items:
type: string
scan_reliability:
$ref: '#/components/schemas/ScanReliabilityOutput'
metadata:
$ref: '#/components/schemas/MetadataOutput'
required:
- url
- final_url
- scan_duration_ms
- risk_score
- risk_level
- banner
- findings
- cookies
- trackers
- third_party_domains
- warnings
- metadata
additionalProperties: false
ScanEvidenceArtifact:
type: object
properties:
kind:
type: string
artifact_id:
type: string
storage_key:
type: string
file_name:
type: string
content_type:
type: string
sha256:
type: string
bytes:
type: integer
required:
- kind
- artifact_id
- storage_key
- file_name
- content_type
- sha256
- bytes
additionalProperties: false
ScanEvidenceOutput:
type: object
properties:
anchored_at:
oneOf:
- type: string
format: date-time
- type: 'null'
expires_at:
oneOf:
- type: string
format: date-time
- type: 'null'
evidence_retention_days:
type:
- integer
- 'null'
legal_hold:
type: boolean
legal_hold_reason:
type:
- string
- 'null'
legal_hold_created_at:
oneOf:
- type: string
format: date-time
- type: 'null'
artifact_count:
type: integer
artifacts:
type: array
items:
$ref: '#/components/schemas/ScanEvidenceArtifact'
required:
- anchored_at
- expires_at
- evidence_retention_days
- legal_hold
- artifact_count
- artifacts
additionalProperties: false
ScanQueuedStateResponse:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [queued]
url:
type: string
format: uri
viewport:
type: string
enum: [desktop, mobile]
created_at:
type: string
format: date-time
required:
- id
- status
- url
- viewport
- created_at
additionalProperties: false
ScanRunningStateResponse:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [running]
url:
type: string
format: uri
viewport:
type: string
enum: [desktop, mobile]
created_at:
type: string
format: date-time
required:
- id
- status
- url
- viewport
- created_at
additionalProperties: false
ScanFailedStateResponse:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [failed]
url:
type: string
format: uri
viewport:
type: string
enum: [desktop, mobile]
error:
type: string
created_at:
type: string
format: date-time
evidence:
$ref: '#/components/schemas/ScanEvidenceOutput'
required:
- id
- status
- url
- viewport
- error
- created_at
- evidence
additionalProperties: false
ScanCompletedStateResponse:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
enum: [completed]
url:
type: string
format: uri
viewport:
type: string
enum: [desktop, mobile]
created_at:
type: string
format: date-time
duration_ms:
type: integer
risk_score:
type:
- integer
- 'null'
risk_level:
type:
- string
- 'null'
result:
$ref: '#/components/schemas/ScanResultOutput'
evidence:
$ref: '#/components/schemas/ScanEvidenceOutput'
required:
- id
- status
- url
- viewport
- created_at
- result
- evidence
additionalProperties: false
ScanStatusResponse:
oneOf:
- $ref: '#/components/schemas/ScanQueuedStateResponse'
- $ref: '#/components/schemas/ScanRunningStateResponse'
- $ref: '#/components/schemas/ScanFailedStateResponse'
- $ref: '#/components/schemas/ScanCompletedStateResponse'
DriftAlert:
type: object
properties:
type:
type: string
enum:
- new_tracker_detected
- tracker_removed
- new_preconsent_cookie
- cookie_category_changed
- banner_configuration_changed
- reject_button_missing
- reject_button_added
- new_third_party_domain
- risk_score_regression
- risk_score_improvement
- new_finding_type
- finding_resolved
severity:
type: string
enum: [high, medium, low, info]
title:
type: string
description:
type: string
key:
type: string
details:
type: object
additionalProperties: true
required:
- type
- severity
- title
- description
additionalProperties: false
ScanDiff:
type: object
properties:
score_change:
type: integer
alerts:
type: array
items:
$ref: '#/components/schemas/DriftAlert'
summary:
type: string
required:
- score_change
- alerts
- summary
additionalProperties: false
ScanCompareSnapshot:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
created_at:
type: string
format: date-time
risk_score:
type:
- integer
- 'null'
findings_count:
type: integer
evidence:
$ref: '#/components/schemas/ScanEvidenceOutput'
required:
- id
- url
- created_at
- risk_score
- findings_count
- evidence
additionalProperties: false
ScanCompareDelta:
type: object
properties:
risk_score:
type:
- integer
- 'null'
findings_count:
type:
- integer
- 'null'
new_findings:
type: array
items:
type: string
resolved_findings:
type: array
items:
type: string
required:
- risk_score
- findings_count
- new_findings
- resolved_findings
additionalProperties: false
ReleaseMarker:
type: object
properties:
id:
type: string
format: uuid
label:
type: string
version:
type:
- string
- 'null'
description:
type:
- string
- 'null'
released_at:
type: string
format: date-time
source:
type: string
enum: [manual, api]
metadata:
type: object
additionalProperties: true
created_at:
type: string
format: date-time
required:
- id
- label
- version
- description
- released_at
- source
- metadata
- created_at
additionalProperties: false
ReleaseCorrelation:
type: object
properties:
marker:
$ref: '#/components/schemas/ReleaseMarker'
status:
type: string
enum: [awaiting_scan, regression, stable, improvement]
confidence:
type: string
enum: [high, medium, low]
reason:
type: string
disclaimer:
type: string
regression_detected:
type: boolean
hours_to_first_scan:
type:
- number
- 'null'
marker_overlap_count:
type: integer
baseline_scan_id:
type:
- string
- 'null'
baseline_scan_created_at:
oneOf:
- type: string
format: date-time
- type: 'null'
correlated_scan_id:
type:
- string
- 'null'
correlated_scan_created_at:
oneOf:
- type: string
format: date-time
- type: 'null'
current_scan_is_first_post_release:
type: boolean
score_delta:
type:
- integer
- 'null'
diff_summary:
type:
- string
- 'null'
diff_alert_types:
type: array
items:
type: string
regression_alert_id:
type:
- string
- 'null'
regression_alert_severity:
type:
- string
- 'null'
required:
- marker
- status
- confidence
- reason
- disclaimer
- regression_detected
- hours_to_first_scan
- marker_overlap_count
- baseline_scan_id
- baseline_scan_created_at
- correlated_scan_id
- correlated_scan_created_at
- current_scan_is_first_post_release
- score_delta
- diff_summary
- diff_alert_types
- regression_alert_id
- regression_alert_severity
additionalProperties: false
ScanCompareResponse:
type: object
properties:
baseline_kind:
type: string
enum: [latest]
current:
$ref: '#/components/schemas/ScanCompareSnapshot'
baseline:
oneOf:
- $ref: '#/components/schemas/ScanCompareSnapshot'
- type: 'null'
delta:
$ref: '#/components/schemas/ScanCompareDelta'
diff:
oneOf:
- $ref: '#/components/schemas/ScanDiff'
- type: 'null'
release_correlation:
oneOf:
- $ref: '#/components/schemas/ReleaseCorrelation'
- type: 'null'
required:
- baseline_kind
- current
- baseline
- delta
- diff
- release_correlation
additionalProperties: false
ScanDiffSnapshot:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
created_at:
type: string
format: date-time
risk_score:
type:
- integer
- 'null'
evidence:
$ref: '#/components/schemas/ScanEvidenceOutput'
required:
- id
- url
- created_at
- risk_score
- evidence
additionalProperties: false
ScanDiffResponse:
type: object
properties:
previous:
$ref: '#/components/schemas/ScanDiffSnapshot'
current:
$ref: '#/components/schemas/ScanDiffSnapshot'
diff:
$ref: '#/components/schemas/ScanDiff'
release_correlation:
oneOf:
- $ref: '#/components/schemas/ReleaseCorrelation'
- type: 'null'
required:
- previous
- current
- diff
- release_correlation
additionalProperties: false
NumericMetricBenchmark:
type: object
properties:
value:
type: number
percentile:
type: number
average:
type: number
best_in_class:
type: number
required:
- value
- percentile
- average
- best_in_class
additionalProperties: false
BooleanMetricBenchmark:
type: object
properties:
value:
type: boolean
percentage_true:
type: number
required:
- value
- percentage_true
additionalProperties: false
ScanBenchmarkResponse:
type: object
properties:
available:
type: boolean
sample_size:
type: integer
period:
type: string
enum: [all_time, last_90d]
last_computed:
oneOf:
- type: string
format: date-time
- type: 'null'
reason:
type: string
enum: [insufficient_sample, benchmark_unavailable]
risk_score:
$ref: '#/components/schemas/NumericMetricBenchmark'
pre_consent_cookie_count:
$ref: '#/components/schemas/NumericMetricBenchmark'
pre_consent_tracker_count:
$ref: '#/components/schemas/NumericMetricBenchmark'
third_party_domain_count:
$ref: '#/components/schemas/NumericMetricBenchmark'
has_banner:
$ref: '#/components/schemas/BooleanMetricBenchmark'
has_reject:
$ref: '#/components/schemas/BooleanMetricBenchmark'
reject_suppressed:
$ref: '#/components/schemas/BooleanMetricBenchmark'
required:
- available
- sample_size
- period
- last_computed
additionalProperties: false
RiskScoreTrendPoint:
type: object
properties:
scan_id:
type: string
format: uuid
date:
type: string
format: date-time
risk_score:
type: integer
scanner_version:
type: string
required:
- scan_id
- date
- risk_score
- scanner_version
additionalProperties: false
RiskScoreTrendSeries:
type: object
properties:
viewport:
type: string
enum: [desktop, mobile]
trend:
type: string
enum: [improving, degrading, stable, insufficient_data]
scanner_versions:
type: array
items:
type: string
total_points:
type: integer
returned_points:
type: integer
truncated_points:
type: integer
points:
type: array
items:
$ref: '#/components/schemas/RiskScoreTrendPoint'
required:
- viewport
- trend
- scanner_versions
- total_points
- returned_points
- truncated_points
- points
additionalProperties: false
RiskScoreTrendResponse:
type: object
properties:
url:
type: string
format: uri
days:
oneOf:
- type: integer
enum: [30, 90, 365]
- type: 'null'
excluded_scan_count:
type: integer
series:
type: array
items:
$ref: '#/components/schemas/RiskScoreTrendSeries'
required:
- url
- days
- excluded_scan_count
- series
additionalProperties: false
LastScan:
type: object
properties:
id:
type: string
format: uuid
status:
type: string
risk_score:
type:
- integer
- 'null'
risk_level:
type:
- string
- 'null'
created_at:
type: string
format: date-time
required:
- id
- status
- risk_score
- risk_level
- created_at
additionalProperties: false
MonitorAlertConfig:
type: object
properties:
minimum_severity:
type: string
enum: [high, medium, low, info]
enabled_types:
type: array
items:
type: string
required:
- minimum_severity
- enabled_types
additionalProperties: false
Monitor:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
interval_hours:
type: integer
evidence_retention_days:
type:
- integer
- 'null'
enabled:
type: boolean
next_run_at:
type: string
format: date-time
last_error:
type:
- string
- 'null'
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
last_scan:
oneOf:
- $ref: '#/components/schemas/LastScan'
- type: 'null'
alert_count:
type: integer
alert_config:
$ref: '#/components/schemas/MonitorAlertConfig'
required:
- id
- url
- interval_hours
- evidence_retention_days
- enabled
- next_run_at
- last_error
- created_at
- updated_at
- last_scan
- alert_count
- alert_config
additionalProperties: false
MonitorUpsertRequest:
type: object
properties:
url:
type: string
format: uri
interval_hours:
type: integer
minimum: 1
maximum: 720
enabled:
type: boolean
required:
- url
- interval_hours
additionalProperties: false
MonitorPatchRequest:
type: object
properties:
interval_hours:
type: integer
minimum: 1
maximum: 720
enabled:
type: boolean
evidence_retention_days:
oneOf:
- type: integer
minimum: 7
maximum: 3650
- type: 'null'
alert_config:
$ref: '#/components/schemas/MonitorAlertConfig'
minProperties: 1
additionalProperties: false
MonitorListPagination:
type: object
properties:
page:
type: integer
page_size:
type: integer
total:
type: integer
total_pages:
type: integer
has_prev:
type: boolean
has_next:
type: boolean
required:
- page
- page_size
- total
- total_pages
- has_prev
- has_next
additionalProperties: false
MonitorListResponse:
type: object
properties:
monitors:
type: array
items:
$ref: '#/components/schemas/Monitor'
pagination:
$ref: '#/components/schemas/MonitorListPagination'
required:
- monitors
- pagination
additionalProperties: false
MonitorMutationResponse:
type: object
properties:
ok:
type: boolean
monitor:
$ref: '#/components/schemas/Monitor'
required:
- ok
- monitor
additionalProperties: false
ReleaseMarkerCreateRequest:
type: object
properties:
label:
type: string
maxLength: 120
version:
type: string
maxLength: 80
description:
type: string
maxLength: 500
released_at:
type: string
format: date-time
metadata:
type: object
additionalProperties: true
required:
- label
additionalProperties: false
ReleaseMarkersListResponse:
type: object
properties:
markers:
type: array
items:
$ref: '#/components/schemas/ReleaseMarker'
correlations:
type: array
items:
$ref: '#/components/schemas/ReleaseCorrelation'
required:
- markers
- correlations
additionalProperties: false
ReleaseMarkerMutationResponse:
type: object
properties:
ok:
type: boolean
marker:
$ref: '#/components/schemas/ReleaseMarker'
required:
- ok
- marker
additionalProperties: false
Webhook:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
active:
type: boolean
events:
type: array
minItems: 1
items:
type: string
enum: [scan_complete, regression_alert]
last_used_at:
oneOf:
- type: string
format: date-time
- type: 'null'
last_status:
type:
- string
- 'null'
last_error:
type:
- string
- 'null'
updated_at:
type: string
format: date-time
required:
- id
- url
- active
- events
- last_used_at
- last_status
- last_error
- updated_at
additionalProperties: false
WebhookConfigResponse:
type: object
properties:
webhook:
oneOf:
- $ref: '#/components/schemas/Webhook'
- type: 'null'
required:
- webhook
additionalProperties: false
WebhookUpsertRequest:
type: object
properties:
url:
type: string
format: uri
events:
type: array
minItems: 1
items:
type: string
enum: [scan_complete, regression_alert]
active:
type: boolean
rotate_secret:
type: boolean
required:
- url
additionalProperties: false
WebhookUpsertResponse:
type: object
properties:
ok:
type: boolean
created:
type: boolean
webhook:
$ref: '#/components/schemas/Webhook'
signing_secret:
type: string
description: Returned only on create or when `rotate_secret=true`.
required:
- ok
- created
- webhook
additionalProperties: false
WebhookPayloadScan:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
monitor_id:
type:
- string
- 'null'
risk_score:
type:
- integer
- 'null'
risk_level:
type:
- string
- 'null'
regression_message:
type:
- string
- 'null'
regression_details:
type: object
additionalProperties: true
required:
- id
- url
additionalProperties: false
WebhookPayload:
type: object
properties:
event_type:
type: string
enum: [scan_complete, regression_alert]
sent_at:
type: string
format: date-time
scan:
$ref: '#/components/schemas/WebhookPayloadScan'
required:
- event_type
- sent_at
- scan
additionalProperties: false
DisputeCreateRequest:
type: object
properties:
scan_id:
type: string
format: uuid
finding_type:
type: string
finding_identifier:
type: string
reason:
type: string
required:
- scan_id
- finding_type
- finding_identifier
additionalProperties: false
DisputeCreateResponse:
type: object
properties:
dispute_id:
type: string
format: uuid
status:
type: string
required:
- dispute_id
- status
additionalProperties: false
PortfolioDomain:
type: object
properties:
id:
type: string
format: uuid
url:
type: string
format: uri
interval_hours:
type: integer
enabled:
type: boolean
cmp:
type:
- string
- 'null'
risk_score:
type:
- integer
- 'null'
risk_level:
type:
- string
- 'null'
score_delta:
type:
- integer
- 'null'
last_scan_at:
oneOf:
- type: string
format: date-time
- type: 'null'
last_completed_scan_at:
oneOf:
- type: string
format: date-time
- type: 'null'
last_scan_status:
type:
- string
- 'null'
last_scan_error:
type:
- string
- 'null'
pending_alerts_count:
type: integer
open_alerts_count:
type: integer
oldest_open_issue_at:
oneOf:
- type: string
format: date-time
- type: 'null'
next_run_at:
type: string
format: date-time
overdue_seconds:
type: integer
monitor_status:
type: string
enum: [active, paused, error, pending]
sla_status:
type: string
enum: [healthy, at_risk, breached, paused, pending]
benchmark_context:
oneOf:
- $ref: '#/components/schemas/PortfolioBenchmarkContext'
- type: 'null'
required:
- id
- url
- interval_hours
- enabled
- cmp
- risk_score
- risk_level
- score_delta
- last_scan_at
- last_completed_scan_at
- last_scan_status
- last_scan_error
- pending_alerts_count
- open_alerts_count
- oldest_open_issue_at
- next_run_at
- overdue_seconds
- monitor_status
- sla_status
additionalProperties: false
PortfolioBenchmarkContext:
type: object
properties:
percentile:
type: integer
previous_percentile:
type:
- integer
- 'null'
change:
type:
- integer
- 'null'
direction:
type: string
enum: [improving, worsening, stable, insufficient_data]
sample_size:
type: integer
last_computed:
oneOf:
- type: string
format: date-time
- type: 'null'
required:
- percentile
- previous_percentile
- change
- direction
- sample_size
- last_computed
additionalProperties: false
PortfolioSummary:
type: object
properties:
total:
type: integer
matching_total:
type: integer
high:
type: integer
medium:
type: integer
low:
type: integer
pending:
type: integer
errors:
type: integer
paused:
type: integer
alerts_pending:
type: integer
sla_breached:
type: integer
benchmarked_domains:
type: integer
peer_outliers:
type: integer
top_decile:
type: integer
worsening_domains:
type: integer
improving_domains:
type: integer
required:
- total
- matching_total
- high
- medium
- low
- pending
- errors
- paused
- alerts_pending
- sla_breached
additionalProperties: false
PortfolioPagination:
type: object
properties:
page:
type: integer
page_size:
type: integer
total:
type: integer
total_pages:
type: integer
has_prev:
type: boolean
has_next:
type: boolean
required:
- page
- page_size
- total
- total_pages
- has_prev
- has_next
additionalProperties: false
PortfolioFilters:
type: object
properties:
page:
type: integer
page_size:
type: integer
sort:
type: string
enum: [risk_score, last_scan_at, url, alerts, sla_status, aging, benchmark_percentile, benchmark_change]
order:
type: string
enum: [asc, desc]
risk_level:
type: string
enum: [all, high, medium, low, unknown]
monitor_status:
type: string
enum: [all, active, paused, error, pending]
has_alerts:
type: boolean
cmp:
type: string
q:
type: string
required:
- page
- page_size
- sort
- order
- risk_level
- monitor_status
- has_alerts
- cmp
- q
additionalProperties: false
PortfolioMeta:
type: object
properties:
available_cmps:
type: array
items:
type: string
benchmarks:
$ref: '#/components/schemas/PortfolioBenchmarkMeta'
required:
- available_cmps
additionalProperties: false
PortfolioBenchmarkMeta:
type: object
properties:
available:
type: boolean
sample_size:
type: integer
last_computed:
oneOf:
- type: string
format: date-time
- type: 'null'
required:
- available
- sample_size
- last_computed
additionalProperties: false
PortfolioResponse:
type: object
properties:
summary:
$ref: '#/components/schemas/PortfolioSummary'
domains:
type: array
items:
$ref: '#/components/schemas/PortfolioDomain'
pagination:
$ref: '#/components/schemas/PortfolioPagination'
filters:
$ref: '#/components/schemas/PortfolioFilters'
meta:
$ref: '#/components/schemas/PortfolioMeta'
required:
- summary
- domains
- pagination
- filters
- meta
additionalProperties: false
PublicStatusIncident:
type: object
properties:
id:
type: string
title:
type: string
message:
type: string
impact:
type: string
enum: [degraded, outage]
status:
type: string
enum: [investigating, identified, monitoring, resolved]
started_at:
type: string
format: date-time
resolved_at:
oneOf:
- type: string
format: date-time
- type: 'null'
updated_at:
type: string
format: date-time
required:
- id
- title
- message
- impact
- status
- started_at
- resolved_at
- updated_at
additionalProperties: false
CMPHealth:
type: object
properties:
cmp:
type: string
total:
type: integer
detected:
type: integer
detection_rate:
type: number
status:
type: string
enum: [operational, degraded]
degraded_since:
oneOf:
- type: string
format: date-time
- type: 'null'
recovering_since:
oneOf:
- type: string
format: date-time
- type: 'null'
required:
- cmp
- total
- detected
- detection_rate
- status
- degraded_since
- recovering_since
additionalProperties: false
QueueDepth:
type: object
properties:
pending:
type: integer
processing:
type: integer
required:
- pending
- processing
additionalProperties: false
TimedMetric:
type: object
properties:
last_1h:
type:
- number
- 'null'
last_24h:
type:
- number
- 'null'
required:
- last_1h
- last_24h
additionalProperties: false
PublicUptimeDay:
type: object
properties:
day:
type: string
format: date-time
status:
type: string
enum: [operational, degraded, outage]
required:
- day
- status
additionalProperties: false
PublicStatusPayload:
type: object
properties:
generated_at:
type: string
format: date-time
overall_status:
type: string
enum: [operational, degraded, outage]
scanner_status:
type: string
enum: [operational, degraded, outage]
queue_depth:
$ref: '#/components/schemas/QueueDepth'
success_rate:
$ref: '#/components/schemas/TimedMetric'
average_duration_ms:
$ref: '#/components/schemas/TimedMetric'
cmp_detection:
type: array
items:
$ref: '#/components/schemas/CMPHealth'
incidents:
type: array
items:
$ref: '#/components/schemas/PublicStatusIncident'
uptime_90d:
type: array
items:
$ref: '#/components/schemas/PublicUptimeDay'
required:
- generated_at
- overall_status
- scanner_status
- queue_depth
- success_rate
- average_duration_ms
- cmp_detection
- incidents
- uptime_90d
additionalProperties: false
paths:
/api/v1/scan:
post:
tags: [Scans]
operationId: createScan
summary: Submit a scan
description: Queue a new scan for the requested URL and viewport.
security:
- bearerAuth: []
x-required-scopes:
- scan:write
x-rate-limit:
requests: 100
window_seconds: 60
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScanCreateRequest'
responses:
'201':
description: Scan queued.
headers:
Location:
schema:
type: string
description: Relative URL for polling the queued scan.
content:
application/json:
schema:
$ref: '#/components/schemas/ScanSubmissionResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'503':
$ref: '#/components/responses/ServiceUnavailable'
'500':
$ref: '#/components/responses/InternalError'
/api/v1/scan/{scanId}:
get:
tags: [Scans]
operationId: getScanStatus
summary: Get scan status or final result
description: Returns only scans owned by the authenticated API key owner.
security:
- bearerAuth: []
x-required-scopes:
- scan:read
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- $ref: '#/components/parameters/ScanID'
- in: query
name: locale
required: false
schema:
type: string
description: Optional finding localization hint.
- in: query
name: jurisdiction
required: false
schema:
type: string
description: Optional enforcement-context jurisdiction override.
responses:
'200':
description: Current scan state.
content:
application/json:
schema:
$ref: '#/components/schemas/ScanStatusResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/scan/{scanId}/compare:
get:
tags: [Scans]
operationId: compareScanToLatestBaseline
summary: Compare a completed scan to the latest previous same-host baseline
security:
- bearerAuth: []
x-required-scopes:
- scan:read
x-rate-limit:
requests: 30
window_seconds: 60
parameters:
- $ref: '#/components/parameters/ScanID'
- in: query
name: baseline
required: false
schema:
type: string
enum: [latest]
description: Only `latest` is currently supported.
responses:
'200':
description: Baseline comparison payload.
content:
application/json:
schema:
$ref: '#/components/schemas/ScanCompareResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/Unprocessable'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/scan/{scanId}/diff:
get:
tags: [Scans]
operationId: diffTwoCompletedScans
summary: Diff two completed scans for the same host
security:
- bearerAuth: []
x-required-scopes:
- scan:read
x-rate-limit:
requests: 30
window_seconds: 60
parameters:
- $ref: '#/components/parameters/ScanID'
- in: query
name: previous
required: true
schema:
type: string
format: uuid
description: Previous completed scan UUID from the same host.
responses:
'200':
description: Granular drift payload.
content:
application/json:
schema:
$ref: '#/components/schemas/ScanDiffResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'422':
$ref: '#/components/responses/Unprocessable'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/scan/{scanId}/benchmarks:
get:
tags: [Scans]
operationId: getScanBenchmarks
summary: Get benchmark context for a completed scan
security:
- bearerAuth: []
x-required-scopes:
- scan:read
x-rate-limit:
requests: 30
window_seconds: 60
parameters:
- $ref: '#/components/parameters/ScanID'
responses:
'200':
description: Benchmark payload.
content:
application/json:
schema:
$ref: '#/components/schemas/ScanBenchmarkResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/scan/trend:
get:
tags: [Scans]
operationId: getRiskScoreTrend
summary: Get risk-score trend history for a URL
security:
- bearerAuth: []
x-required-scopes:
- scan:read
x-rate-limit:
requests: 30
window_seconds: 60
parameters:
- in: query
name: url
required: true
schema:
type: string
format: uri
description: Target URL to aggregate by normalized scan URL.
- in: query
name: days
required: false
schema:
type: string
enum: ['30', '90', '365', all]
description: Trend window. Omit or use `all` for all retained completed scans.
responses:
'200':
description: Trend response grouped by viewport.
content:
application/json:
schema:
$ref: '#/components/schemas/RiskScoreTrendResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/monitors:
get:
tags: [Monitoring]
operationId: listMonitors
summary: List monitors
security:
- bearerAuth: []
x-required-scopes:
- monitors:read
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- in: query
name: page
required: false
schema:
type: integer
minimum: 1
default: 1
- in: query
name: page_size
required: false
schema:
type: integer
minimum: 1
maximum: 50
default: 10
responses:
'200':
description: Paginated monitors.
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorListResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
post:
tags: [Monitoring]
operationId: upsertMonitor
summary: Create or update a monitor by URL
security:
- bearerAuth: []
x-required-scopes:
- monitors:write
x-rate-limit:
requests: 100
window_seconds: 60
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorUpsertRequest'
responses:
'200':
description: Monitor created or updated.
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorMutationResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/monitors/{monitorId}:
get:
tags: [Monitoring]
operationId: getMonitor
summary: Get a monitor
security:
- bearerAuth: []
x-required-scopes:
- monitors:read
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
responses:
'200':
description: Monitor detail.
content:
application/json:
schema:
$ref: '#/components/schemas/Monitor'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
patch:
tags: [Monitoring]
operationId: patchMonitor
summary: Update a monitor
security:
- bearerAuth: []
x-required-scopes:
- monitors:write
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorPatchRequest'
responses:
'200':
description: Updated monitor.
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorMutationResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
delete:
tags: [Monitoring]
operationId: deleteMonitor
summary: Delete a monitor
security:
- bearerAuth: []
x-required-scopes:
- monitors:write
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
responses:
'200':
description: Monitor deleted.
content:
application/json:
schema:
$ref: '#/components/schemas/OKResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/monitors/{monitorId}/release-markers:
get:
tags: [Release Markers]
operationId: listReleaseMarkers
summary: List release markers and correlations for a monitor
security:
- bearerAuth: []
x-required-scopes:
- monitors:read
x-rate-limit:
requests: 60
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
- in: query
name: limit
required: false
schema:
type: integer
minimum: 1
description: Positive integer capped server-side.
responses:
'200':
description: Marker and correlation list.
content:
application/json:
schema:
$ref: '#/components/schemas/ReleaseMarkersListResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
post:
tags: [Release Markers]
operationId: createReleaseMarker
summary: Record a release marker for a monitor
description: Use release markers to correlate regressions with deployments.
security:
- bearerAuth: []
x-required-scopes:
- monitors:write
x-rate-limit:
requests: 60
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ReleaseMarkerCreateRequest'
responses:
'201':
description: Release marker created.
content:
application/json:
schema:
$ref: '#/components/schemas/ReleaseMarkerMutationResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/monitors/{monitorId}/release-markers/{markerId}:
delete:
tags: [Release Markers]
operationId: deleteReleaseMarker
summary: Delete a release marker
security:
- bearerAuth: []
x-required-scopes:
- monitors:write
x-rate-limit:
requests: 30
window_seconds: 60
parameters:
- $ref: '#/components/parameters/MonitorID'
- $ref: '#/components/parameters/MarkerID'
responses:
'200':
description: Marker deleted.
content:
application/json:
schema:
$ref: '#/components/schemas/OKResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/webhooks:
get:
tags: [Webhooks]
operationId: getWebhookConfig
summary: Get webhook configuration
security:
- bearerAuth: []
x-required-scopes:
- webhooks:read
x-rate-limit:
requests: 100
window_seconds: 60
responses:
'200':
description: Current webhook configuration or `null`.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookConfigResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
post:
tags: [Webhooks]
operationId: upsertWebhookConfig
summary: Create or update webhook configuration
description: |-
Register an outbound webhook endpoint for scan completion and regression alerts.
Delivery headers:
- `X-GDPRMonitor-Event`
- `X-GDPRMonitor-Timestamp`
- `X-GDPRMonitor-Signature`
- `X-GDPRMonitor-Delivery-ID`
Signature base string: `timestamp + "." + raw_json_body`
security:
- bearerAuth: []
x-required-scopes:
- webhooks:write
x-rate-limit:
requests: 100
window_seconds: 60
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookUpsertRequest'
callbacks:
webhookDelivery:
'{$request.body#/url}':
post:
operationId: deliverWebhookEvent
summary: Outbound webhook delivery
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookPayload'
examples:
scan_complete:
$ref: '#/components/examples/WebhookScanComplete'
regression_alert:
$ref: '#/components/examples/WebhookRegressionAlert'
responses:
'200':
description: Return any 2xx response to acknowledge delivery.
responses:
'200':
description: Webhook created or updated.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookUpsertResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
delete:
tags: [Webhooks]
operationId: deleteWebhookConfig
summary: Delete webhook configuration
security:
- bearerAuth: []
x-required-scopes:
- webhooks:write
x-rate-limit:
requests: 100
window_seconds: 60
responses:
'200':
description: Webhook deleted.
content:
application/json:
schema:
$ref: '#/components/schemas/OKResponse'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/disputes:
post:
tags: [Disputes]
operationId: createDispute
summary: Create a dispute for a scan finding
security:
- bearerAuth: []
x-required-scopes:
- scan:write
x-rate-limit:
requests: 100
window_seconds: 60
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DisputeCreateRequest'
responses:
'200':
description: Dispute created.
content:
application/json:
schema:
$ref: '#/components/schemas/DisputeCreateResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/portfolio:
get:
tags: [Portfolio]
operationId: getPortfolio
summary: Get portfolio overview across monitored domains
security:
- bearerAuth: []
x-required-scopes:
- monitors:read
x-rate-limit:
requests: 100
window_seconds: 60
parameters:
- in: query
name: page
schema:
type: integer
minimum: 1
default: 1
- in: query
name: page_size
schema:
type: integer
minimum: 1
maximum: 100
default: 50
- in: query
name: sort
schema:
type: string
enum: [risk_score, last_scan_at, url, alerts, sla_status, aging, benchmark_percentile, benchmark_change]
- in: query
name: order
schema:
type: string
enum: [asc, desc]
- in: query
name: risk_level
schema:
type: string
enum: [all, high, medium, low, unknown]
- in: query
name: monitor_status
schema:
type: string
enum: [all, active, paused, error, pending]
- in: query
name: has_alerts
schema:
type: boolean
- in: query
name: cmp
schema:
type: string
- in: query
name: q
schema:
type: string
- in: query
name: format
schema:
type: string
enum: [csv]
description: When `csv`, the endpoint returns `text/csv` instead of JSON.
responses:
'200':
description: Portfolio JSON or CSV export.
content:
application/json:
schema:
$ref: '#/components/schemas/PortfolioResponse'
text/csv:
schema:
type: string
description: CSV export of portfolio domains.
'401':
$ref: '#/components/responses/Unauthorized'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
$ref: '#/components/responses/InternalError'
'503':
$ref: '#/components/responses/ServiceUnavailable'
/api/v1/status:
get:
tags: [Status]
operationId: getPublicStatus
summary: Get public scanner status
description: Public, cached status endpoint. No API key required.
security: []
x-rate-limit:
per_known_ip_requests: 60
per_unknown_shared_requests: 20
window_seconds: 60
responses:
'200':
description: Public service health payload.
content:
application/json:
schema:
$ref: '#/components/schemas/PublicStatusPayload'
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: Status generation failed.
content:
application/json:
schema:
$ref: '#/components/schemas/APIError'
'503':
$ref: '#/components/responses/ServiceUnavailable'