Appearance
Findings
List, inspect, and manage audit findings. A finding is a security, performance, accessibility, or SEO issue discovered during a scan.
Base URL: https://app.krafter.dev/api/v1
List Findings
Retrieve all findings for a project with optional filtering by severity, status, or domain.
GET /orgs/:org_id/projects/:project_id/audit/findings?severity=...&status=...&domain=...&limit=...Required scope: audit:read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
severity | string | No | Filter by severity. One of critical, high, medium, low. |
status | string | No | Filter by status. One of new, triaged, in_progress, ready_verify, resolved, dismissed. |
domain | string | No | Filter by domain. One of security, performance, accessibility, seo. |
limit | integer | No | Page size. Defaults to the server limit. |
offset | integer | No | Number of records to skip. Findings list uses offset, not cursor pagination. |
Pagination
The findings list endpoint always returns meta.next_cursor: null — it does not yet emit a cursor token. Use limit + offset to page through results, or read meta.total to size your queries.
Findings are ordered by priority_score descending, then inserted_at descending, then id descending.
Example Request
bash
curl "https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/findings?severity=high&status=new&limit=20" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": [
{
"id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"org_id": "11111111-1111-1111-1111-111111111111",
"project_id": "production-website",
"domain": "security",
"title": "Missing Content-Security-Policy header",
"description": "The application does not set a Content-Security-Policy header.",
"severity": "high",
"status": "new",
"confidence": 0.95,
"priority": {
"impact": 80,
"effort": 20,
"risk": 90,
"score": 88
},
"owner_id": null,
"affected_assets": [
{ "type": "url", "value": "https://example.com" }
],
"evidence": [],
"recommended_fix": {
"summary": "Add a Content-Security-Policy header to all responses.",
"steps": ["Configure CSP at the reverse proxy", "Start with a restrictive policy", "Relax as needed"],
"estimated_minutes": 60
},
"created_at": "2025-06-10T14:30:00Z",
"updated_at": "2025-06-10T14:30:00Z"
}
],
"meta": {
"request_id": "F-mxbAQk6m9lXeQAAATj",
"total": 8,
"next_cursor": null
},
"error": null
}Notes on the response shape:
confidenceis a float between0.0and1.0reflecting how confident the scanner is that the finding is real.prioritycontains four integer scores (impact,effort,risk,score), each in the0..100range.scoreis the composite priority used to order findings.owner_idis the user UUID that owns the finding, ornullif unassigned.affected_assetsis an array of{type, value}items collected from the scanner. The shape of each item depends on the scanner —type: "url"is the most common.evidenceis an array of evidence records (see below). It is empty when the scanner did not attach evidence artefacts.recommended_fixis always present withsummary,steps, andestimated_minuteskeys; values default to empty string / empty list /0when the scanner did not produce a recommendation.
Evidence item shape
Each entry in evidence follows this shape:
json
{
"id": "ev_a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"type": "screenshot",
"artifact_url": "https://artifacts.krafter.dev/audit/...",
"checksum": "sha256:abcd...",
"captured_at": "2025-06-10T14:30:00Z"
}Get Finding
Retrieve a single finding by ID.
GET /orgs/:org_id/projects/:project_id/audit/findings/:finding_idRequired scope: audit:read
Example Request
bash
curl https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/findings/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": {
"id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"org_id": "11111111-1111-1111-1111-111111111111",
"project_id": "production-website",
"domain": "security",
"title": "Missing Content-Security-Policy header",
"description": "The application does not set a Content-Security-Policy header.",
"severity": "high",
"status": "new",
"confidence": 0.95,
"priority": {
"impact": 80,
"effort": 20,
"risk": 90,
"score": 88
},
"owner_id": null,
"affected_assets": [
{ "type": "url", "value": "https://example.com" }
],
"evidence": [],
"recommended_fix": {
"summary": "Add a Content-Security-Policy header to all responses.",
"steps": ["Configure CSP at the reverse proxy", "Start with a restrictive policy", "Relax as needed"],
"estimated_minutes": 60
},
"created_at": "2025-06-10T14:30:00Z",
"updated_at": "2025-06-10T14:30:00Z"
},
"meta": {
"request_id": "G-nyc8sPL2vXfRBBBEUk"
},
"error": null
}Error Response
json
// 404 Not Found
{
"data": null,
"meta": {
"request_id": "G-nyc8sPL2vXfRBBBEUk"
},
"error": {
"code": "not_found"
}
}Update Finding
Update the status, severity, or owner of a finding. Only provided fields are changed; the request must include at least one field or it returns 422 invalid_params.
PATCH /orgs/:org_id/projects/:project_id/audit/findings/:finding_idRequired scope: audit:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
status | string | No | New status. One of new, triaged, in_progress, ready_verify, resolved, dismissed. |
severity | string | No | New severity. One of critical, high, medium, low. |
owner_id | string | No | UUID of the user to assign as owner. Pass an empty string to clear the assignment. |
Tags are not supported
Findings have no tags field. The schema only carries domain, severity, status, scoring fields, and owner_id. Use owner_id and the tasks API to organise work.
Example Request
bash
curl -X PATCH https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/findings/a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"status": "triaged",
"owner_id": "22222222-2222-2222-2222-222222222222"
}'Example Response
The response is the full finding shape (same as Get Finding), reflecting the applied changes.
Error Responses
json
// 422 Unprocessable Entity — invalid status/severity, or empty patch
{
"data": null,
"meta": {
"request_id": "H-pzd9tQM3wYgSCCCFVl"
},
"error": {
"code": "invalid_params"
}
}json
// 404 Not Found
{
"data": null,
"meta": {
"request_id": "H-pzd9tQM3wYgSCCCFVl"
},
"error": {
"code": "not_found"
}
}Bulk Update Findings
Update multiple findings at once. Applies the same patch to every finding in finding_ids.
POST /orgs/:org_id/projects/:project_id/audit/findings/bulkRequired scope: audit:write
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
finding_ids | string[] | Yes | Array of finding UUIDs. Non-string entries are filtered; duplicates are de-duplicated. Empty arrays return 422 invalid_params. |
patch | object | Yes | Fields to update on each finding. Must contain at least one of the fields below. |
patch.status | string | No | New status. Same enum as Update Finding. |
patch.severity | string | No | New severity. Same enum as Update Finding. |
patch.owner_id | string | No | UUID of the user to assign as owner. |
Example Request
bash
curl -X POST https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/findings/bulk \
-H "Authorization: Bearer kr_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"finding_ids": [
"a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
"c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f"
],
"patch": {
"status": "dismissed"
}
}'Example Response
json
{
"data": {
"updated_count": 3,
"requested_count": 3,
"ids": [
"a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e",
"c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f"
]
},
"meta": {
"request_id": "I-qae0uRN4xZhTDDDGWm"
},
"error": null
}updated_count reflects how many records were actually updated within the project scope. requested_count is the de-duplicated input size. ids lists the IDs that matched the project scope and were updated. IDs that belong to a different project (or do not exist) are silently dropped — compare requested_count against updated_count to detect mismatches.
List Regressions
Retrieve regressions tracked against findings in the project. A regression record is created when a previously-resolved finding is detected again.
GET /orgs/:org_id/projects/:project_id/audit/regressions?cursor=...&limit=...Required scope: audit:read
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
cursor | string | No | Pagination cursor from a previous response. |
limit | integer | No | Page size. Defaults to the server limit. |
Regressions are ordered by inserted_at descending, then id descending.
Example Request
bash
curl "https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/regressions?limit=10" \
-H "Authorization: Bearer kr_live_abc123def456"Example Response
json
{
"data": [
{
"id": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
"finding_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"severity": "high",
"trigger": "scan",
"status": "open",
"impacted_assets": [
{ "type": "url", "value": "https://example.com" }
],
"opened_at": "2025-06-10T14:30:00Z",
"closed_at": null,
"created_at": "2025-06-10T14:30:00Z",
"updated_at": "2025-06-10T14:30:00Z"
}
],
"meta": {
"request_id": "J-rbf1vSO5yajUEEEHXn",
"total": 1,
"next_cursor": null
},
"error": null
}status is open while the regression is active and closed once it is resolved again. closed_at is null while status is open.