BETA
Skip to content

Scans

Run security scans and view the command center dashboard. Scans analyze your URLs for security vulnerabilities, performance issues, and accessibility problems.

Base URL: https://app.krafter.dev/api/v1

Run Scan

Trigger a new security scan for a given URL. The scan is queued and processed asynchronously.

POST /orgs/:org_id/projects/:project_id/audit/scans/run

Required scope: audit:write

Rate limit (strict)

This endpoint uses the strict bucket30 requests / 60 seconds per team, shared with flags/{evaluate,bootstrap,track} and push subscriber registration. Exceeding the limit returns 429 Too Many Requests with the headers retry-after, x-ratelimit-limit, x-ratelimit-remaining, and x-ratelimit-reset, and a body of { "error": "Rate limit exceeded", "retry_after": <seconds> }. Note that this body is not wrapped in the standard Audit response envelope.

Request Body

FieldTypeRequiredDescription
urlstringYesURL to scan. Must be http/https and target a public host. See URL validation below.
triggerstringNoWhat triggered the scan. One of manual, scheduled, post_deploy. Defaults to manual.

URL validation (SSRF protection)

url must use the http or https scheme. Requests targeting any of the following are rejected with 422 Unprocessable Entity and error.code: "invalid_params":

  • localhost, 127.0.0.1, ::1, 0.0.0.0
  • Hosts ending in .local or .internal
  • IPv4 private ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
  • IPv4 link-local: 169.254.0.0/16
  • IPv6 unique-local: fd00::/8
  • IPv6 link-local: fe80::/10

Example Request

bash
curl -X POST https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/scans/run \
  -H "Authorization: Bearer kr_live_abc123def456" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "trigger": "manual"
  }'

Example Response

json
// 202 Accepted
{
  "data": {
    "id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "org_id": "11111111-1111-1111-1111-111111111111",
    "project_id": "production-website",
    "trigger": "manual",
    "status": "queued",
    "summary": {},
    "started_at": "2025-06-10T14:30:00Z",
    "finished_at": null,
    "inserted_at": "2025-06-10T14:30:00Z",
    "updated_at": "2025-06-10T14:30:00Z"
  },
  "meta": {
    "request_id": "F-mxbAQk6m9lXeQAAATj"
  },
  "error": null
}

status is one of queued, running, completed, failed. trigger is one of manual, scheduled, post_deploy. The summary map is populated once the scan finishes and contains aggregated finding counts by domain and severity. The url you submitted is not echoed back in the response — track scans by id.

Error Response

json
// 422 Unprocessable Entity
{
  "data": null,
  "meta": {
    "request_id": "F-mxbAQk6m9lXeQAAATj"
  },
  "error": {
    "code": "invalid_params"
  }
}

Command Center

Retrieve the command center dashboard overview with finding summaries, trending issues, and overall health status.

GET /orgs/:org_id/projects/:project_id/audit/command-center?date_from=...&date_to=...

Required scope: audit:read

Query Parameters

ParameterTypeRequiredDescription
date_fromstringNoStart date for the dashboard period (ISO 8601).
date_tostringNoEnd date for the dashboard period (ISO 8601).

Example Request

bash
curl "https://app.krafter.dev/api/v1/orgs/:org_id/projects/:project_id/audit/command-center?date_from=2025-06-01T00:00:00Z&date_to=2025-06-10T23:59:59Z" \
  -H "Authorization: Bearer kr_live_abc123def456"

Example Response

json
{
  "data": {
    "org_id": "11111111-1111-1111-1111-111111111111",
    "project_id": "production-website",
    "period": {
      "from": "2025-06-04",
      "to": "2025-06-10"
    },
    "health_score": 67,
    "unresolved_findings": 11,
    "critical_findings": 2,
    "top_priorities": [
      {
        "id": "d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f9a",
        "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": "critical",
        "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"
      }
    ],
    "expected_impact": {
      "cwv_improvement_pct": 14,
      "security_risk_reduction_pct": 27,
      "regression_risk_pct": 16
    }
  },
  "meta": {
    "request_id": "F-mxbAQk6m9lXeQAAATj"
  },
  "error": null
}

health_score is computed from unresolved + critical counts (100 minus a penalty, capped at 95 penalty). top_priorities returns up to 5 unresolved findings ordered by priority.score then by created_at. expected_impact is a projected impact estimate, not a measurement. period defaults to the last 7 days when date_from/date_to are omitted.

Built by Krafter Studio