Safety
The Safety Centre grade (A to E), its checks and fixes, and the public badge.
| Operation | Method and path |
|---|---|
| Safety Centre grade and checks | GET /v1/projects/{project}/safety |
| Re-run every safety check now | POST /v1/projects/{project}/safety/scan |
| Apply a finding’s one-click fix (or open an AI fix PR) | POST /v1/projects/{project}/safety/checks/{check_id}/fix |
| Acknowledge a finding (accept the risk) | POST /v1/projects/{project}/safety/checks/{check_id}/acknowledge |
| Public badge summary | GET /v1/public/safety/{badge_id} |
| Public badge image | GET /v1/public/safety/{badge_id}/badge.svg |
Safety Centre grade and checks
Section titled “Safety Centre grade and checks”GET /v1/projects/{project}/safety- Operation:
getSafetyReport - Scopes:
safety:read
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
environment |
query | string | Defaults to production. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. | SafetyReport |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/projects/shopfront/safety \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "project_id": "string", "environment": "string", "grade": "A", "score": 0, "summary": "Grade B. One thing to fix: members without passkeys.", "counts": { "pass": 1, "warn": 1, "fail": 1 }, "checks": [ { "id": "chk_01jbx2m9k7q4r8t5v6w3y0z1ck", "category": "secrets_in_bundle", "status": "fail", "severity": "critical", "title": "Secret visible in your website's code", "finding": "Your SUPABASE_SERVICE_ROLE_KEY is visible to anyone visiting your site.", "environment": "production", "resource": { "type": "service", "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv", "name": "web" }, "blocks_deploy": true, "fix": [ { "kind": "manual", "label": "Rotate the key in Supabase, then update it here", "command": "guv secrets rotate SUPABASE_SERVICE_ROLE_KEY --env production" }, { "kind": "open_pr", "label": "Ask AI to move the key server-side (opens a PR)" } ], "acknowledged": null, "detected_at": "2026-10-23T14:03:00Z", "resolved_at": null } ], "badge": { "enabled": false, "url": "https://guv.dev/", "svg_url": "https://guv.dev/" }, "checked_at": "2026-10-22T13:05:00Z"}Re-run every safety check now
Section titled “Re-run every safety check now”POST /v1/projects/{project}/safety/scan- Operation:
runSafetyScan - Scopes:
safety:read - Idempotency-Key: required
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
Responses
| Status | Meaning | Body |
|---|---|---|
202 Accepted |
Accepted. The work continues in the background; follow the operation. | Operation |
Errors: 400, 401, 403, 404, 409, 422, 429, 500, as problem+json with a stable code.
Example
curl -X POST https://api.guv.dev/v1/projects/shopfront/safety/scan \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)"Example response
{ "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op", "kind": "deployment.create", "status": "running", "summary": "Deploying web to production", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en", "resource": { "type": "deployment", "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp", "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp" }, "progress": { "current_step": "build", "completed_steps": 1, "total_steps": 6, "percent": 20 }, "steps": [ { "name": "resolve", "title": "Reading your settings", "status": "succeeded", "attempt": 1, "started_at": "2026-10-23T14:02:00Z", "finished_at": "2026-10-23T14:02:01Z" }, { "name": "build", "title": "Building your app", "status": "running", "attempt": 1, "started_at": "2026-10-23T14:02:01Z", "finished_at": null } ], "created_by": { "type": "user", "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us", "display": "Stewart Webb" }, "created_at": "2026-10-23T14:02:00Z", "links": { "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op", "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events", "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp" }}Apply a finding’s one-click fix (or open an AI fix PR)
Section titled “Apply a finding’s one-click fix (or open an AI fix PR)”POST /v1/projects/{project}/safety/checks/{check_id}/fix- Operation:
applySafetyFix - Scopes:
safety:write - Approval: In environments with protection turned on (usually production), changes by agents and by roles outside
approver_rolesreturnapproval_required. - Idempotency-Key: required; supports
?dry_run=true
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
check_id * |
path | string | Safety check id. |
action |
query | integer | Index into the check’s fix array. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
Dry run. Nothing was changed; this is what would happen. | Plan |
202 Accepted |
Accepted. The work continues in the background; follow the operation. | Operation |
Errors: 400, 401, 403, 404, 409, 422, 429, 500, as problem+json with a stable code.
Example
curl -X POST https://api.guv.dev/v1/projects/shopfront/safety/checks/che_01jbx2m9k7q4r8t5v6w3y0z1pr/fix \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)"Example response
{ "id": "op_01jbx2m9k7q4r8t5v6w3y0z1op", "kind": "deployment.create", "status": "running", "summary": "Deploying web to production", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en", "resource": { "type": "deployment", "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp", "url": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp" }, "progress": { "current_step": "build", "completed_steps": 1, "total_steps": 6, "percent": 20 }, "steps": [ { "name": "resolve", "title": "Reading your settings", "status": "succeeded", "attempt": 1, "started_at": "2026-10-23T14:02:00Z", "finished_at": "2026-10-23T14:02:01Z" }, { "name": "build", "title": "Building your app", "status": "running", "attempt": 1, "started_at": "2026-10-23T14:02:01Z", "finished_at": null } ], "created_by": { "type": "user", "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us", "display": "Stewart Webb" }, "created_at": "2026-10-23T14:02:00Z", "links": { "self": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op", "events": "/v1/operations/op_01jbx2m9k7q4r8t5v6w3y0z1op/events", "resource": "/v1/deployments/dep_01jbx2m9k7q4r8t5v6w3y0z1dp" }}Acknowledge a finding (accept the risk)
Section titled “Acknowledge a finding (accept the risk)”POST /v1/projects/{project}/safety/checks/{check_id}/acknowledgeAcknowledged findings still count towards the grade. Findings that block deploys cannot be acknowledged.
- Operation:
acknowledgeSafetyCheck - Scopes:
safety:write - Idempotency-Key: required; supports
?dry_run=true
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
check_id * |
path | string | Safety check id. |
Request body (application/json, required): SafetyAcknowledge
| Field | Type | Description |
|---|---|---|
reason * |
string | |
until |
string (date-time) |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. With dry_run=true: a Plan (application/vnd.guv.plan+json) and nothing changes. |
SafetyCheck |
Errors: 400, 401, 403, 404, 409, 422, 429, 500, as problem+json with a stable code.
Example
curl -X POST https://api.guv.dev/v1/projects/shopfront/safety/checks/che_01jbx2m9k7q4r8t5v6w3y0z1pr/acknowledge \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "reason": "string", "until": "2026-10-22T13:05:00Z"}'Example response
{ "id": "chk_01jbx2m9k7q4r8t5v6w3y0z1ck", "category": "secrets_in_bundle", "status": "fail", "severity": "critical", "title": "Secret visible in your website's code", "finding": "Your SUPABASE_SERVICE_ROLE_KEY is visible to anyone visiting your site.", "environment": "production", "resource": { "type": "service", "id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv", "name": "web" }, "blocks_deploy": true, "fix": [ { "kind": "manual", "label": "Rotate the key in Supabase, then update it here", "command": "guv secrets rotate SUPABASE_SERVICE_ROLE_KEY --env production" }, { "kind": "open_pr", "label": "Ask AI to move the key server-side (opens a PR)" } ], "acknowledged": null, "detected_at": "2026-10-23T14:03:00Z", "resolved_at": null}Public badge summary
Section titled “Public badge summary”GET /v1/public/safety/{badge_id}- Operation:
getPublicSafetySummary - Scopes: none (public)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
badge_id * |
path | string | Public, unguessable badge id of a project. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. | SafetyBadgeSummary |
Errors: 400, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/public/safety/bad_01jbx2m9k7q4r8t5v6w3y0z1pr \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "project_name": "string", "grade": "A", "checked_at": "2026-10-22T13:05:00Z", "data_location": "London, UK", "categories": [ { "category": "secrets_in_code", "label": "string", "status": "pass" } ]}Public badge image
Section titled “Public badge image”GET /v1/public/safety/{badge_id}/badge.svg- Operation:
getPublicSafetyBadge - Scopes: none (public)
Parameters
| Name | In | Type | Description |
|---|---|---|---|
badge_id * |
path | string | Public, unguessable badge id of a project. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK | string |
Errors: 400, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/public/safety/bad_01jbx2m9k7q4r8t5v6w3y0z1pr/badge.svg \ -H "Authorization: Bearer $GUV_TOKEN"Example response
"string"