List Findings
List every detected security finding for a domain, with CVE/CVSS/EPSS data, full affected-asset detail, and the sector's average breach cost as industry context.
/api/domain/{domain_name}/findingsBearer <token>API key authentication — use your Cyborux API key as a Bearer token.
Overview
Returns every security finding the analysis engine has persisted for a domain. Each row carries:
- Classification —
severity(critical/high/medium/low/info),category(vulnerability, infrastructure, email_security, …), and the rule-enginefinding_type. - Status timeline —
status(new/acknowledged/false_positive/resolved/ignored),detected_at,verified_at. - Affected asset — polymorphic reference (
type+identifier). Theidentifieris always returned; it isnullonly for domain-level findings that have no specific asset. - CVE block — when the underlying finding is vulnerability-class, a flat
cveobject is surfaced with theidentifier(CVE / GHSA / OSV / WPSCAN id), CVSS score and vector, EPSS probability + percentile, CISA KEV membership (in_kev), and exploit/patch availability flags. The response also includes asummaryblock (faceted counts — see below) and afinancial_impactblock carrying the average cost of a breach in the organization's sector, so a dashboard can render the risk distribution and its industry context without paginating through every row. Findings carry no monetary value of their own.
Asset visibility
Affected-asset identifiers and identifier-bearing evidence keys (subdomain names, email addresses, file paths, usernames, person names, internal IPs, phone numbers, CNAME targets) are returned in full to any user with access to the domain — everything the platform surfaces is public OSINT, so there is no ownership wall.
Per-row title and description come from the localised findings catalog (see language), not from the DB. That keeps the copy generic and produces natural Spanish/Catalan copy when language=es|ca.
CVE, CVSS, EPSS, technology name + version, breach counts, and externally observable signals (server header, TLS posture, DMARC/SPF reasons, etc.) are likewise visible.
Facet counts
Each facet's chip counts ignore that facet's own selection, so a UI can render counts that mean "what the list would become if I picked this filter":
summary.by_severityandsummary.with_cveignore theseverityandwith_cveselections — they count over the domain +status+q+categoryset.summary.by_categoryignores thecategoryselection — it counts over the domain +status+q+severity+with_cveset.summary.totaland the paginatedfindings[]reflect the fully-filtered set.
Sector-average breach cost
Methodology version: ibm-2025-eur-v3.
financial_impact.sector_average_eur is the average cost of a breach that the
IBM Cost of a Data Breach Report 2025 published for the organization's sector,
converted at the ECB 2025 average USD→EUR rate (0.92) and rounded to the
nearest EUR 100,000:
sector_average_eur = round(baseline_eur(sector))One lookup, no multiplications. It is industry context, not a measurement of
the domain's risk — no finding, no severity and no headcount enters it, so it
is invariant across this endpoint's filters and identical for two domains in
the same sector. Findings themselves carry no estimated_impact_eur: pricing a
finding at a fraction of a sector baseline treated an ordinal severity as a
probability that was never measured.
- Sector baselines come from the IBM per-industry average breach cost, keyed by a sector taxonomy that collapses NIS2 Annex I + II onto IBM's industry buckets. A user-supplied sector is resolved by exact match or synonym.
- Unresolved sectors read the global average across all sectors (
other) and are flagged:sector_knownisfalseandcaveatscarriessector-fallback-other. Surface that flag — the figure is then not the caller's sector's. employee_tieris organization context for display only; it does not scale the figure.
Figures are model-derived estimates, not actuarial losses — they exclude regulatory fines (GDPR / NIS2: up to EUR 10 M or 2 % of turnover) and ransom payments, which can dwarf the recovery cost the catalog represents.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
domain_name | string | The domain name (e.g. example.com). |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
severity | string | none | Comma-list, allow-listed against critical,high,medium,low,info. Garbage values return 422. |
category | string | none | Comma-list of categories. One of: vulnerability, infrastructure, exposure, authentication, email_security, credential_exposure, encryption, web_security, dns_security, misconfiguration, reconnaissance. |
status | string | active | active ≡ new+acknowledged. all disables filtering. Any single status value also accepted. |
with_cve | boolean | false | Restrict to findings whose evidence names at least one advisory — exactly the findings that come back with a non-null cve block. |
language | string | en | Output language for findings[].title and findings[].description. One of en, es, ca. Unknown values fall back to en. |
q | string | none | Case-insensitive substring match on the finding title (max 200 chars). |
limit | integer | 100 | Page size, clamped to [1, 500]. |
offset | integer | 0 | Number of rows to skip from the start of the filtered set. |
Response
{
"findings": [
{
"id": 18342,
"finding_type": "subdomain_takeover",
"severity": "high",
"category": "infrastructure",
"title": "Subdomain Takeover Risk",
"description": "One or more subdomains point to external services that are no longer claimed or have been decommissioned. These dangling DNS records can be exploited by attackers to take control of the subdomain.",
"status": "new",
"detected_at": "2026-05-01T10:00:00Z",
"verified_at": "2026-05-05T08:00:00Z",
"affected_asset": { "type": "subdomain", "identifier": "legacy.example.com" },
"cve": null,
"evidence": {
"cname": "abandoned.s3.amazonaws.com",
"is_external_cname": true
},
"conditions_met": ["cname_external", "no_active_target"]
}
],
"summary": {
"total": 87,
"by_severity": { "critical": 5, "high": 12, "medium": 30, "low": 30, "info": 10 },
"by_category": { "vulnerability": 14, "email_security": 10, "infrastructure": 18 },
"with_cve": 14,
"page": { "limit": 100, "offset": 0, "next_offset": null }
},
"financial_impact": {
"sector_key": "finance",
"sector_label": "Finance",
"sector_known": true,
"employee_tier": "1000+",
"sector_average_eur": 5100000,
"currency": "EUR",
"methodology_version": "ibm-2025-eur-v3",
"caveats": [
"estimate-not-actuarial",
"excludes-fines-and-ransom",
"usd-to-eur-2025-ecb"
]
}
}| Status | Detail |
|---|---|
401 | Not authenticated. |
403 | Forbidden — you do not have access to this domain. |
404 | Domain not found. |
422 | Validation error — e.g. severity contained a non-allow-listed value. |
Response Fields
findingsarrayPage of finding rows, ordered by severity (critical first) then detected_at desc.
findings[].idintegerStable identifier for the finding row.
findings[].finding_typestringRule-engine identifier (e.g. vulnerable_technology, subdomain_takeover, email_spoofable). Stable across analyses.
findings[].severitystringOne of critical, high, medium, low, info.
findings[].categorystringOne of the 12 categories listed under Query Parameters.
findings[].statusstringnew, acknowledged, false_positive, resolved, or ignored.
findings[].affected_assetobject{ type, identifier, component }. identifier is always populated, except for domain-level findings with no specific asset, where it is null.
component names the part of that asset the finding is about — the technology on a host, such as openssl on app.example.com. It sits beside the identifier rather than inside it, because the identifier is rendered verbatim as the name of a thing you own. null for findings that are about the asset as a whole.
Findings of a type that carries a component are one row per (asset, component) pair: a host running two vulnerable technologies returns two rows, not one.
findings[].cveobject | nullPresent when the finding's evidence carries a vulnerability identifier. Fields: identifier, cvss_score, cvss_vector, epss_score, epss_percentile, exploit_available, in_kev, patch_available. null otherwise. in_kev is membership of CISA's Known Exploited Vulnerabilities catalogue; together with epss_score at or above 0.7 it is what promotes a vulnerability finding to critical.
findings[].evidenceobject | nullStructured supporting context — identifier-bearing keys (subdomain, email, filename, username, person_name, cname, phones, private_ips, …) alongside CVE-bearing keys and externally observable signals (server header, breach names, TLS posture, DMARC reasons, etc.). All keys are returned.
For a vulnerability-class finding, evidence.cves carries the full advisory population, worst first, in the same shape as the cve block above. Derived figures are not sent beside it: the number of advisories is the length of that list and a severity breakdown is a fold over it. Two undocumented fields, cve_count and severity_histogram, were emitted alongside it in earlier responses and have been removed — a second copy of a number is a second place for it to go stale. Count and group evidence.cves instead.
findings[].conditions_metarray | nullRule conditions that triggered the finding. null when the finding records none.
summaryobjectAggregate counts over the filtered result set (so filter chips and histograms stay coherent).
summary.totalintegerTotal number of findings matching the filters.
summary.by_severityobjectCount per severity. Keys: critical, high, medium, low, info.
summary.by_categoryobjectCount per category present in the filtered set.
summary.with_cveintegerCount of findings whose evidence names at least one advisory — the same set with_cve=true returns.
summary.pageobject{ limit, offset, next_offset }. next_offset is null when the last page is reached.
financial_impactobjectThe organization's sector average breach cost — industry context, not the domain's risk. Fields: sector_key, sector_label, sector_known, employee_tier, sector_average_eur, currency, methodology_version, caveats. sector_known: false (with the sector-fallback-other caveat) means the sector did not resolve and the figure is the global average across all sectors.
curl -X GET "https://api.cyborux.com/api/domain/example.com/findings?severity=critical,high&with_cve=true" \
-H "Authorization: Bearer YOUR_API_KEY"const url = new URL(
"https://api.cyborux.com/api/domain/example.com/findings"
);
url.searchParams.set("severity", "critical,high");
url.searchParams.set("with_cve", "true");
const res = await fetch(url, {
headers: { Authorization: "Bearer YOUR_API_KEY" },
});
const { findings, summary, financial_impact } = await res.json();import requests
response = requests.get(
"https://api.cyborux.com/api/domain/example.com/findings",
params={"severity": "critical,high", "with_cve": True},
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
payload = response.json()
findings = payload["findings"]
sector_average_eur = payload["financial_impact"]["sector_average_eur"]