Automate around local scan evidence.
Use the API to trigger scans, fetch findings, export reports, and feed results into weekly review, tickets, or internal dashboards. The surface stays small on purpose, but it is broad enough for real automation.
Quick start
Run one read-only scan locally, read the findings, then export a report for review. That sequence is enough for most teams to automate the first useful workflow.
http://127.0.0.1:<configured-port>/v1
curl "http://127.0.0.1:8080/v1/findings?provider=aws&status=open"
What the API covers
- Start and inspect scan jobs from the desktop app or an internal orchestrator.
- Read findings, account metadata, provider summaries, and compatibility information.
- Export PDF, CSV, JSON, or text evidence packages for finance and engineering review.
- Deliver signed webhook callbacks when event-driven automation is needed.
Complete example: weekly waste handoff
Problem: finance asks why AWS network cost is still rising after the team says cleanup was finished. The useful API workflow is not one call; it is scan, filter, export, then notify the owner with evidence.
/v1/scansStart a scoped scan for the AWS account and network waste policy.
/v1/findings?provider=aws&category=network&status=openRead only open network findings, ranked by confidence and estimated monthly impact.
/v1/reports/exportCreate a PDF for review and JSON for the ticketing or weekly governance system.
curl -X POST "http://127.0.0.1:8080/v1/scans" \
-H "Authorization: Bearer $CWS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"provider":"aws","account_id":"prod-shared-services","policy_scope":["network-waste"]}'
import time
import requests
base = "http://127.0.0.1:8080/v1"
headers = {"Authorization": f"Bearer {token}"}
scan = requests.post(
f"{base}/scans",
headers=headers,
json={
"provider": "aws",
"account_id": "prod-shared-services",
"policy_scope": ["network-waste"],
},
).json()
while True:
status = requests.get(f"{base}/scans/{scan['id']}", headers=headers).json()
if status["state"] in ("completed", "failed"):
break
time.sleep(5)
findings = requests.get(
f"{base}/findings?provider=aws&category=network&status=open",
headers=headers,
).json()
export = requests.post(
f"{base}/reports/export",
headers=headers,
json={
"format": ["pdf", "json"],
"finding_ids": [item["id"] for item in findings["items"][:10]],
"purpose": "weekly-network-waste-review",
},
).json()
print(export["pdf_path"], export["json_path"])
const base = "http://127.0.0.1:8080/v1";
const headers = { Authorization: `Bearer ${token}`, "Content-Type": "application/json" };
const scan = await fetch(`${base}/scans`, {
method: "POST",
headers,
body: JSON.stringify({
provider: "aws",
account_id: "prod-shared-services",
policy_scope: ["network-waste"]
})
}).then((res) => res.json());
const findings = await fetch(`${base}/findings?provider=aws&category=network&status=open`, { headers }).then((res) => res.json());
const exportData = await fetch(`${base}/reports/export`, {
method: "POST",
headers,
body: JSON.stringify({ format: ["pdf", "json"], finding_ids: findings.items.slice(0, 10).map((item) => item.id), purpose: "weekly-network-waste-review" })
}).then((res) => res.json());
console.log(exportData.pdf_path, exportData.json_path);
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
const base = "http://127.0.0.1:8080/v1"
payload := map[string]any{
"provider": "aws",
"account_id": "prod-shared-services",
"policy_scope": []string{"network-waste"},
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", base+"/scans", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
_ = req
fmt.Println("scan started")
}
| Output | How the team uses it |
|---|---|
pdf_path | Attach to the weekly review note for finance and owners. |
json_path | Feed into an internal ticket, dashboard, or AI evidence skill. |
finding_ids | Keep cleanup scoped to reviewed findings, not broad provider deletion. |
Common endpoints
Discover capabilities
Use these first when building an internal client or checking whether a local app version supports the workflow you need.
/v1/openapi.jsonFetch the OpenAPI document for local integration and client generation.
/v1/meta/compatibilityRead app and API compatibility metadata before calling newer fields or routes.
Run and monitor scans
Trigger a scoped scan from an internal job, then poll state until findings and exports are ready.
/v1/scansStart a scan using a configured provider account and policy scope.
/v1/scans/{id}Check scan state, high-level counts, and whether export artifacts are ready.
/v1/findingsList findings with provider, status, severity, category, team, and cursor filters.
Build governance views
Pull enough summarized data for weekly review without copying every raw finding into another system.
/v1/reports/trendsPull weekly trend lines for review cadence, closure, and savings movement.
/v1/reports/categoriesSummarize the taxonomy behind the findings so teams can compare like with like.
/v1/reports/teamsSee backlog and closure by team, owner, or business unit.
Export and automate handoff
Create evidence artifacts and test event delivery before connecting cleanup decisions to internal systems.
/v1/reports/exportGenerate PDF, CSV, JSON, or text exports for tickets, weekly review, or audit notes.
/v1/webhooks/testVerify webhook delivery and signature handling before wiring production automation.
Language examples
The same API can be called from scripts, jobs, or internal tools. These examples stay intentionally small.
curl -H "Authorization: Bearer $CWS_TOKEN" \
"http://127.0.0.1:8080/v1/findings?provider=aws&status=open"
const res = await fetch("http://127.0.0.1:8080/v1/reports/trends?group_by=week", {
headers: { Authorization: `Bearer ${token}` }
});
import requests
res = requests.get(
"http://127.0.0.1:8080/v1/reports/categories",
headers={"Authorization": f"Bearer {token}"},
)
req, _ := http.NewRequest("GET", "http://127.0.0.1:8080/v1/scans/123", nil)
req.Header.Set("Authorization", "Bearer "+token)
Webhook signing
Webhook payloads use HMAC-SHA256 over the raw body and timestamp. Keep the signing secret inside the internal receiver and reject stale timestamps.
| Header | Meaning |
|---|---|
X-CWS-Timestamp | Event timestamp used to limit replay. |
X-CWS-Signature | HMAC signature computed over the body and timestamp. |
X-CWS-Event | Event type for the receiving system. |
Errors and retries
- Use cursor pagination for large result sets.
- Retry transient network failures with backoff, not tight loops.
- Check compatibility metadata before depending on new fields.
- Prefer read-only scan access until your cleanup workflow is approved.
Safe integration
- Do not expose the local API directly to the public internet.
- Keep automation scoped to reviewed findings rather than broad deletion.
- Attach report exports to tickets or review notes before cleanup.
- Store tokens only in trusted internal systems.