Skip to main content

Findings API

Findings are the core output of Infracast's compliance engine. Each finding represents a rule violation — a specific resource failing a compliance check against one or more frameworks. Findings include the affected resource, the control it violates, severity, and a remediation recommendation.


Endpoints​

MethodPathDescriptionPermission
GET/api/v1/tenants/{tenantID}/findingsList findings with filtersfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}Get a findingfindings:read
POST/api/v1/tenants/{tenantID}/findings/runRun the audit engineaudit:run
GET/api/v1/tenants/{tenantID}/findings/scoredFindings with risk scoresfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}/remediationAI remediation stepsfindings:read
GET/api/v1/tenants/{tenantID}/findings/{findingID}/terraform-patchTerraform fixfindings:read
POST/api/v1/tenants/{tenantID}/findings/{findingID}/acceptAccept risk for one findingaudit:run
DELETE/api/v1/tenants/{tenantID}/findings/{findingID}/acceptRevoke a single-finding acceptanceaudit:run
POST/api/v1/tenants/{tenantID}/findings/groups/{groupID}/acceptAccept risk for a whole root-cause groupaudit:run
DELETE/api/v1/tenants/{tenantID}/findings/groups/{groupID}/acceptRevoke a group acceptanceaudit:run
GET/api/v1/tenants/{tenantID}/risk-acceptancesList acceptances (both scopes)findings:read
POST/api/v1/tenants/{tenantID}/findings/{findingID}/resolveMark resolvedfindings:write

List Findings​

GET /api/v1/tenants/{tenantID}/findings

Returns a paginated, filterable list of compliance findings.

Query Parameters​

ParameterTypeDescription
frameworkstringFilter by framework ID (e.g., nist-800-53, cis-aws, pci-dss)
severitystringComma-separated severity levels: CRITICAL,HIGH,MEDIUM,LOW,INFO
statusstringopen, resolved, accepted, all (default: open)
resource_typestringFilter by node type (e.g., aws.s3.bucket)
control_idstringFilter by control ID (e.g., AC-2, 5.2)
searchstringSearch in title, description, or resource ID
pageintPage number (default: 1)
per_pageintResults per page (default: 50, max: 500)
sortstringSort field: severity, created_at, resource_id. Prefix with - for descending

Example Requests​

# All critical and high findings across all frameworks
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?severity=CRITICAL,HIGH"

# NIST 800-53 findings, sorted by severity
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?framework=nist-800-53&sort=severity"

# Open PCI DSS findings for S3 buckets
curl -H "Authorization: Bearer $TOKEN" \
"$API_URL/api/v1/tenants/acme-corp/findings?framework=pci-dss&resource_type=aws.s3.bucket&status=open"

Example Response​

{
"items": [
{
"id": "NIST-AC-2-STALE-ACCESS-KEY-aws:us-east-1:aws.iam.user:svc-deploy",
"rule_id": "NIST-AC-2-STALE-ACCESS-KEY",
"framework": "nist-800-53",
"control_id": "AC-2",
"severity": "HIGH",
"status": "open",
"title": "IAM access key unused for 90+ days",
"description": "IAM user 'svc-deploy' has an access key unused for 127 days (≥90).",
"resource_id": "aws:us-east-1:aws.iam.user:svc-deploy",
"resource_type": "aws.iam.user",
"resource_name": "svc-deploy",
"region": "us-east-1",
"account_id": "123456789012",
"remediation": "Deactivate or delete access keys unused for 90+ days per NIST AC-2.",
"first_seen": "2024-02-15T10:00:00Z",
"last_seen": "2024-03-16T08:30:00Z",
"created_at": "2024-02-15T10:00:00Z"
},
{
"id": "CIS-AWS-5.2-aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"rule_id": "CIS-AWS-5.2",
"framework": "cis-aws",
"control_id": "5.2",
"severity": "HIGH",
"status": "open",
"title": "Security group allows unrestricted SSH access",
"description": "Security group 'web-servers-sg' (sg-0abc123) has an inbound rule permitting SSH (port 22) from 0.0.0.0/0.",
"resource_id": "aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"resource_type": "aws.ec2.security_group",
"resource_name": "web-servers-sg",
"region": "us-east-1",
"account_id": "123456789012",
"remediation": "Remove the 0.0.0.0/0 inbound rule for port 22. Restrict SSH to a bastion host or known admin CIDR ranges.",
"first_seen": "2024-03-10T14:00:00Z",
"last_seen": "2024-03-16T08:30:00Z",
"created_at": "2024-03-10T14:00:00Z"
}
],
"total": 287,
"page": 1,
"per_page": 50,
"total_pages": 6
}

Finding Severity​

Findings are assigned one of five severity levels:

SeverityDescriptionTypical Response Time
CRITICALDirect path to data breach or account compromiseImmediate (hours)
HIGHSignificant control failure, exploitable with low effort24–48 hours
MEDIUMControl gap that increases risk but requires additional factors1–2 weeks
LOWDefense-in-depth gap, minimal immediate risk30–90 days
INFOInformational, no direct riskBacklog / best effort

Framework Mapping​

A single finding can map to multiple framework controls. When a resource fails, Infracast surfaces the cross-framework impact:

GET /api/v1/tenants/{tenantID}/findings?id={findingID}

# Each finding in the response includes cross-framework mappings:
{
"id": "CIS-AWS-5.2-aws:us-east-1:aws.ec2.security_group:sg-0abc123",
"rule_id": "CIS-AWS-5.2",
"framework_mappings": [
{ "framework": "cis-aws", "control_id": "5.2" },
{ "framework": "nist-800-53", "control_id": "SC-7" },
{ "framework": "pci-dss", "control_id": "Req 1.3.2" },
{ "framework": "soc2", "control_id": "CC6.3" }
],
...
}

Run the Audit Engine​

Trigger an immediate audit run against the current infrastructure graph:

POST /api/v1/tenants/{tenantID}/findings/run
{
"framework": "nist-800-53" # Optional: run only one framework
}

# Or run all frameworks
POST /api/v1/tenants/{tenantID}/findings/run
{}

Response:

{
"job_id": "audit-job-abc123",
"status": "running",
"frameworks": ["nist-800-53", "cis-aws", "pci-dss", "soc2"],
"started_at": "2024-03-16T10:00:00Z"
}

Get AI Remediation​

Infracast provides detailed, AI-enhanced remediation steps for each finding:

GET /api/v1/tenants/{tenantID}/findings/{findingID}/remediation
{
"finding_id": "CIS-AWS-5.2-sg-0abc123",
"summary": "Remove the unrestricted SSH inbound rule and restrict to specific CIDR ranges.",
"steps": [
{
"order": 1,
"action": "Identify all EC2 instances using this security group",
"command": "aws ec2 describe-instances --filters 'Name=instance.group-id,Values=sg-0abc123'"
},
{
"order": 2,
"action": "Revoke the unrestricted SSH rule",
"command": "aws ec2 revoke-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 0.0.0.0/0"
},
{
"order": 3,
"action": "Add a restricted rule for your admin CIDR range",
"command": "aws ec2 authorize-security-group-ingress --group-id sg-0abc123 --protocol tcp --port 22 --cidr 10.0.0.0/8"
}
],
"terraform_patch_available": true
}

Get Terraform Patch​

GET /api/v1/tenants/{tenantID}/findings/{findingID}/terraform-patch
{
"finding_id": "CIS-AWS-5.2-sg-0abc123",
"patch": "--- a/security_groups.tf\n+++ b/security_groups.tf\n@@ -15,8 +15,8 @@\n ingress {\n- from_port = 22\n- to_port = 22\n- protocol = \"tcp\"\n- cidr_blocks = [\"0.0.0.0/0\"]\n+ from_port = 22\n+ to_port = 22\n+ protocol = \"tcp\"\n+ cidr_blocks = [\"10.0.0.0/8\"] # Restricted to internal only\n }"
}

Accept Risk​

When a finding cannot be remediated (legacy system, compensating control, business decision), record a risk acceptance. An accepted finding gets status accepted, is excluded from the active counts and compliance scoring, and is listed with status=accepted. Every acceptance has a required reason, an owner, and an expiry (at most 365 days out). When it expires or is revoked the finding is active again.

There are two scopes. They are independent: revoking one never changes the other.

Accept one finding​

POST /api/v1/tenants/{tenantID}/findings/{findingID}/accept
{
"reason": "Legacy jump server scheduled for decommission. Compensating control: security group limited to an isolated VLAN.",
"owner": "carol@example.com",
"expires_at": "2026-12-31T23:59:59Z"
}

This accepts only that finding. Other findings on the same resource, or with the same root cause, are not affected.

StatusMeaning
201Created. Returns the acceptance (scope: "finding", finding_id, owner, reason, expires_at).
400Invalid body (reason or owner missing, expires_at in the past or more than 365 days out), or the finding is not active (resolved findings cannot be accepted).
403Your role cannot accept risk (requires audit:run).
404Unknown finding id, or it belongs to another tenant.
409The finding is already accepted, individually or through its group.

Revoke it with:

DELETE /api/v1/tenants/{tenantID}/findings/{findingID}/accept

Returns 200 with the revoked acceptance, or 404 if the finding has no unrevoked single-finding acceptance. A group acceptance that also covers the finding is not touched.

Accept a root-cause group​

POST /api/v1/tenants/{tenantID}/findings/groups/{groupID}/accept
DELETE /api/v1/tenants/{tenantID}/findings/groups/{groupID}/accept

Same body as above. groupID comes from GET /findings/grouped. The acceptance covers every finding in the group at the time of acceptance. A resource that develops the same problem later is not covered and shows as active (the group then reports state partial). POST returns 201, 400 for validation errors, 404 for an unknown or already-accepted group, and 409 when a live acceptance already exists. DELETE returns 200, or 404 when the group has no unrevoked acceptance.

List acceptances​

GET /api/v1/tenants/{tenantID}/risk-acceptances

Returns every acceptance for the tenant, newest first, with summary counts:

{
"acceptances": [
{
"id": "ra-...",
"scope": "finding",
"finding_id": "f-123",
"group_id": "rc-...",
"group_label": "GuardDuty detector not enabled",
"owner": "carol@example.com",
"reason": "...",
"expires_at": "2026-12-31T23:59:59Z",
"state": "active",
"snapshot": { "finding_ids": ["f-123"], "resource_ids": ["d-1"], "controls": [] }
}
],
"total": 1, "active": 1, "expired": 0, "revoked": 0, "orphaned": 0
}

scope is group or finding. finding_id is set only for finding scope. state is active, expired or revoked.

See accepted findings​

GET /api/v1/tenants/{tenantID}/findings?status=accepted

Returns the findings currently covered by a live acceptance (either scope), with status: "accepted". Use GET /risk-acceptances for who accepted them, why, and until when.


Real-Time Findings Stream​

Subscribe to findings as they are created (Server-Sent Events):

GET /api/v1/tenants/{tenantID}/findings/stream

# Events are streamed as:
data: {"event":"finding.created","finding":{"id":"...","severity":"HIGH",...}}
data: {"event":"finding.resolved","finding_id":"...","resolved_at":"2024-03-16T10:05:00Z"}

Findings Summary by Framework​

GET /api/v1/tenants/{tenantID}/controls/status

{
"overall_score": 83,
"total_findings": 287,
"by_severity": {
"CRITICAL": 3,
"HIGH": 47,
"MEDIUM": 164,
"LOW": 73
},
"by_framework": {
"nist-800-53": { "score": 82, "findings": 94 },
"cis-aws": { "score": 84, "findings": 62 },
"pci-dss": { "score": 88, "findings": 78 },
"soc2": { "score": 91, "findings": 52 }
}
}
API paths corrected 2026-09-14

The previously documented compliance/summary endpoint does not exist. The equivalent endpoint is /api/v1/tenants/{tenantID}/controls/status.


Python Example​

from infracast import InfracastClient

client = InfracastClient(api_url="https://api.infracast.io", api_token="your-token")

# Get all critical findings
findings = client.findings.list(
tenant="acme-corp",
severity=["CRITICAL", "HIGH"],
status="open"
)

# Group by framework
by_framework = {}
for finding in findings:
fw = finding.framework
by_framework.setdefault(fw, []).append(finding)

for framework, fw_findings in by_framework.items():
print(f"{framework}: {len(fw_findings)} critical/high findings")

# Accept a risk (one finding) -- plain REST call
import requests
requests.post(
f"https://api.infracast.io/api/v1/tenants/{tenant_id}/findings/{finding_id}/accept",
headers={"Authorization": "Bearer your-token"},
json={"reason": "Legacy system, decommission scheduled", "owner": "carol@example.com",
"expires_at": "2026-12-31T23:59:59Z"},
)

Next Steps​