API Reference
Complete reference for all ComplianceLayer API endpoints. All requests require authentication via API key unless noted otherwise. Base URL: https://api.compliancelayer.net/v1
Authentication
Send your API key in the Authorization header using the Bearer scheme. The X-API-Key header is supported as an alternative, and a bare key in Authorization without the Bearer prefix is also accepted.
Authorization: Bearer cl_YOUR_API_KEY
# or
X-API-Key: cl_YOUR_API_KEYA few endpoints are public and need no key: POST /scan/free, GET /verify/:report_id, GET /badge/:domain.svg, and GET /pricing/plans. Full details are in the authentication guide.
Error Shape
Every error response is a JSON object with a single detail key. For most errors detail is a string; for 422 request-validation failures it is an array of { field, message } objects. There is no error code, message, or status field in the body.
{
"detail": "Scan job not found"
}See the error handling guide for status-by-status behaviour, including how to distinguish a rate-limit 429 from an exhausted-quota 429.
Scanning
/scanQueue a new scan job for a domain. Returns 202 Accepted with an integer job_id for polling. Most scans complete in roughly 10-30 seconds.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | The domain to scan (e.g., "example.com") |
curl -X POST "https://api.compliancelayer.net/v1/scan" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "example.com"}'/scan/freePublic scanner — no API key required. Runs a scan and returns a limited report synchronously (score, grade, module summaries, and top_issues). Rate limited to 5 scans per hour per IP.
curl -X POST "https://api.compliancelayer.net/v1/scan/free" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"authorization": {"attested": true, "basis": "owner"}
}'
# The authorization object is required on this endpoint. Omitting it, or
# sending "attested": false, returns 403. Basis must be one of:
# owner | written_authorization | authorized_agent/scan/jobs/:job_idGet the status of a scan job. Poll this endpoint until status is "completed", then fetch the full report.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
job_id | integer | The job ID returned from POST /scan |
Response Fields
| Field | Type | Description |
|---|---|---|
job_id | integer | Job identifier |
status | string | queued, running, completed, or failed |
source | string | Where the scan originated (e.g. api, dashboard) |
requested_modules | array | The 15 modules queued for this scan |
failure_reason | string | null | Why the scan failed, when status=failed |
result | object | null | Summary results (only when status=completed) |
curl "https://api.compliancelayer.net/v1/scan/jobs/12345" \
-H "Authorization: Bearer cl_YOUR_API_KEY"/scan/jobs/:job_id/reportGet the complete scan report: per-module results, every issue, and compliance framework mappings. Returns 409 Conflict if the scan hasn't finished yet.
curl "https://api.compliancelayer.net/v1/scan/jobs/12345/report" \
-H "Authorization: Bearer cl_YOUR_API_KEY"Each entry in issues — and in every module's issues array — has exactly three fields: severity, finding, and remediation. Grades are A, B, C, D, or F; there are no + or - variants.
/scan/recentList your most recent scans with summary results. Takes no query parameters.
curl "https://api.compliancelayer.net/v1/scan/recent" \
-H "Authorization: Bearer cl_YOUR_API_KEY"/scan/historyList your scan history. Same response shape as /scan/recent — a scans array plus a total count.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Maximum entries to return (default 50) |
There is no offset, page, or cursor parameter — limit is the only control, so you cannot page beyond the most recent entries.
curl "https://api.compliancelayer.net/v1/scan/history?limit=100" \
-H "Authorization: Bearer cl_YOUR_API_KEY"Batch Operations
/batch/scanScan up to 50 domains in parallel and get them back ranked by risk. This is a quick 4-module assessment (DNS/email, SSL, headers, ports) rather than the full 15-module scan. Each domain consumes one scan from your monthly quota.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domains | array | Yes | 1-50 domains to scan |
sort_by | string | No | risk (worst first, default), score (best first), or domain |
curl -X POST "https://api.compliancelayer.net/v1/batch/scan" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domains": ["vendor1.com", "vendor2.com"],
"sort_by": "risk"
}'/batch/compareCompare 2-5 domains side by side. Returns ranked results with a winner, the score gap, and a per-module comparison. Also a 4-module assessment, and also 1 scan of quota per domain.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domains | array | Yes | 2-5 domains to compare |
Reports & PDF
/compliance/reports/:job_id/pdfGenerate a PDF report for a completed scan. Returns a PDF file with executive summary, module scores, findings, and compliance framework mappings. Available on every plan, including Free — exports are bounded only by your scan quota, since a report can only be generated for a scan you have already run.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
job_id | integer | The completed scan job ID |
# Download PDF report
curl "https://api.compliancelayer.net/v1/compliance/reports/12345/pdf" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
--output report.pdfThe PDF includes:
- Cover page with domain, grade, scan date, and QR verification code
- Executive summary with issue distribution and priority recommendations
- Module-by-module score breakdown (SSL, DNS, headers, ports, etc.)
- Detailed findings per module
- Compliance framework mapping (SOC 2, PCI DSS, HIPAA, NIST CSF, ISO 27001, CIS Controls v8)
Report Verification
/verify/:report_idVerify the authenticity of a ComplianceLayer PDF report. Public endpoint — no authentication required. Used by the QR code embedded in every PDF to let recipients confirm the report is genuine. Rate limited to 20 requests per minute per IP.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
report_id | string | The report ID printed on the PDF. Format CL-XXXXXXXX — the literal prefix CL- followed by 8 uppercase letters and digits. |
# Public endpoint - no API key required
curl "https://api.compliancelayer.net/v1/verify/CL-7K2M9XQ4"A malformed report ID returns the same 404 as an unknown one, so the ID format isn't leaked. There is no valid boolean in the response — a 200 means the report is genuine, a 404 means it isn't.
Badges
/badge/:domain.svgPublic SVG security badge for a domain, suitable for embedding in a README or website. No authentication required. Badges are opt-in: the endpoint serves only domains whose owner has published a badge, and returns 404 otherwise. The default rendering is the ComplianceLayer card; the optional style query parameter accepts flat for a compact shields-style strip.
curl "https://api.compliancelayer.net/v1/badge/example.com.svg?style=flat"/badge/:domain.jsonBadge data as JSON (domain, grade, score, source). Unlike the SVG variant this endpoint requires authentication, and it answers only for domains monitored by your own account. The grade and score are read from the domain's last completed scan.
curl "https://api.compliancelayer.net/v1/badge/example.com.json" \
-H "Authorization: Bearer cl_YOUR_API_KEY"Domain Monitoring
/domainsList all monitored domains for your account, along with how many of your plan's domain slots are in use.
curl "https://api.compliancelayer.net/v1/domains" \
-H "Authorization: Bearer cl_YOUR_API_KEY"/domainsAdd a domain to continuous monitoring with scheduled scans.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
domain | string | Yes | The domain to monitor |
scan_frequency | string | No | hourly, daily, or weekly (default: daily). Anything else returns 400. |
alert_on_score_drop | boolean | No | Whether to raise an alert when the score falls (default: true) |
alert_threshold | integer | No | Score drop that triggers an alert (default: 10) |
curl -X POST "https://api.compliancelayer.net/v1/domains" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": "example.com",
"scan_frequency": "daily",
"alert_on_score_drop": true,
"alert_threshold": 10
}'/domains/:domain_id/badgePublish or unpublish the public security badge for a monitored domain. Badges are opt-in: until published, GET /badge/:domain.svg returns 404 for the domain. See Security Badges.
curl -X PATCH "https://api.compliancelayer.net/v1/domains/42/badge" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"badge_enabled": true}'/domains/:domain_idRemove a domain from monitoring. Stops all scheduled scans. domain_id is the integer id from GET /domains.
/domains/:domain_id/scanTrigger an immediate scan for a monitored domain, outside its regular schedule. Consumes one scan from your monthly quota.
/domains/alertsList alerts for monitored domains (score drops, critical findings, certificate expiry, config changes).
Query Parameters
| Parameter | Type | Description |
|---|---|---|
unread_only | boolean | Return only unread alerts (default: false) |
limit | integer | Maximum alerts to return (default: 50) |
severity | string | Filter by severity |
alert_type | string | Filter by alert type (e.g. critical_issue) |
domain_id | integer | Filter to a single monitored domain |
alerts key. Index into the response directly.curl "https://api.compliancelayer.net/v1/domains/alerts?unread_only=true&limit=20" \
-H "Authorization: Bearer cl_YOUR_API_KEY"/domains/alerts/:alert_id/readMark a single alert as read. alert_id is an integer.
/domains/alerts/mark-all-readMark every alert on the account as read.
/domains/alerts/:alert_idDelete an alert.
/domains/alerts/statsAlert counts for the account: total, unread, by_severity, and by_type.
Settings
/settings/alert-preferencesGet which alert types are enabled: score_drop, critical_issue, cert_expiry, config_change.
/settings/alert-preferencesUpdate alert preferences. Body takes the same four booleans, each defaulting to true.
curl -X PUT "https://api.compliancelayer.net/v1/settings/alert-preferences" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"score_drop": true,
"critical_issue": true,
"cert_expiry": true,
"config_change": false
}'Webhooks
/webhooksList your webhook endpoints.
/webhooksCreate a webhook endpoint. Maximum 1 active endpoint on Free, 5 on paid plans.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | HTTPS URL to receive webhooks |
enabled_events | array | Yes | Event types to subscribe to — at least one. Note the field is enabled_events, not events. |
description | string | No | Optional label, up to 255 characters |
The response includes the generated signing secret along with id, is_active, consecutive_failures, and delivery timestamps.
/webhooks/:endpoint_idGet a single webhook endpoint. endpoint_id is an integer.
/webhooks/:endpoint_idUpdate a webhook endpoint. All fields optional: url, description, enabled_events, is_active.
/webhooks/:endpoint_idDelete a webhook endpoint.
/webhooks/:endpoint_id/testSend a test event to the endpoint. Optional body field event_type (default scan.completed); an unrecognized value returns 400.
/webhooks/:endpoint_id/deliveriesRecent delivery attempts for an endpoint. Optional limit query parameter (default 50).
/webhooks/events/typesList the event types you can subscribe to.
/webhooks/stats/summaryDelivery success and failure counts across your endpoints. Optional hours query parameter (default 24).
Authentication Endpoints
/auth/signupCreate a new account. The response includes your api_key directly, so you can scan immediately without visiting the dashboard.
Request Body
| Parameter | Type | Required |
|---|---|---|
email | string | Yes |
password | string | Yes (min 12 chars) |
/auth/loginAuthenticate with email and password. Returns access_token, token_type, expires_in (86400 seconds), and csrf_token — no user object.
/auth/meGet the current account: id, email, plan, api_key, scans_this_month, scan_limit, scans_remaining, domain_limit, and subscription status.
/auth/api-key/regenerateIssue a new API key. There is one key per account and no create, list, or revoke operation — regenerating invalidates the previous key instantly, with no overlap window.
/auth/logoutInvalidate the current session. Does not affect your API key.
/auth/password/changeChange the password for the signed-in account.
/auth/reset-passwordComplete a password reset using a reset token and a new_password of at least 12 characters.
Usage
/usage/summaryUsage totals for a period. Optional start_date and end_date query parameters in YYYY-MM-DD form.
/usage/limitsYour plan's limits and current consumption, including whether you can still make requests and run scans right now.
/usage/historyMonth-by-month usage history. Optional months query parameter (default 6).
/usage/by-endpointRequest counts broken down per endpoint. Optional days query parameter (default 30).
Billing
/billing/checkoutCreate a Stripe checkout session to start a subscription.
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
plan | string | Yes | One of free, starter, professional, msp, enterprise |
success_url | string | Yes | Where Stripe redirects after a completed checkout |
cancel_url | string | Yes | Where Stripe redirects if checkout is abandoned |
yearly | boolean | No | Bill annually instead of monthly (default: false) |
curl -X POST "https://api.compliancelayer.net/v1/billing/checkout" \
-H "Authorization: Bearer cl_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"plan": "professional",
"success_url": "https://yourapp.com/billing/success",
"cancel_url": "https://yourapp.com/billing/cancel",
"yearly": false
}'Omitting success_url or cancel_url returns 422 — both are required.
/billing/portalCreate a Stripe customer portal session to manage an existing subscription.
/billing/subscriptionCurrent subscription state: plan, status, scans_this_month, scan_limit, and domain_limit.
/pricing/plansPublic list of plans with pricing, scan allowances, rate limits, and feature flags. No authentication required.
Response Codes
| Code | Status | Description |
|---|---|---|
200 | OK | Request succeeded |
202 | Accepted | Scan queued — the success status for POST /scan |
400 | Bad Request | Semantically invalid request (unscannable domain, bad enum value, duplicate resource). Missing or mistyped fields return 422 instead. |
401 | Unauthorized | Missing or invalid API key |
403 | Forbidden | Account disabled, subscription inactive, or feature gated to a higher plan |
404 | Not Found | Resource not found |
405 | Method Not Allowed | Path exists but not for this HTTP method |
409 | Conflict | Report requested before the scan completed |
422 | Unprocessable Entity | Request validation failed — detail is an array of field errors |
429 | Too Many Requests | Rate limit exceeded, or monthly scan quota exhausted |
500 | Internal Server Error | Server error (contact support) |