Projects
A project is one product or monorepo.
| Operation | Method and path |
|---|---|
| List projects | GET /v1/workspaces/{workspace}/projects |
| Create a project | POST /v1/workspaces/{workspace}/projects |
| Get a project | GET /v1/projects/{project} |
| Update a project | PATCH /v1/projects/{project} |
| Delete a project | DELETE /v1/projects/{project} |
| Everything about a project in one call | GET /v1/projects/{project}/status |
| Get a project’s saved canvas layout | GET /v1/projects/{project}/canvas |
| Save a project’s canvas layout | PUT /v1/projects/{project}/canvas |
| Canvas graph for one environment | GET /v1/projects/{project}/environments/{environment}/graph |
| Save canvas node positions | PATCH /v1/projects/{project}/environments/{environment}/graph/layout |
List projects
Section titled “List projects”GET /v1/workspaces/{workspace}/projects- Operation:
listProjects - Scopes:
projects:read - CLI:
guv projects list - Paginated:
?limit=and?cursor=
Parameters
| Name | In | Type | Description |
|---|---|---|---|
workspace * |
path | string | Workspace id (ws_…) or slug. |
q |
query | string |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
A page of Project objects. | ProjectList |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/workspaces/acme/projects \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "data": [ { "id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "slug": "primecarers", "name": "Primecarers", "description": "Care management platform", "region": "lon1", "residency": "uk-only", "classification": "special-category", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "ghi_01jbx2m9k7q4r8t5v6w3y0z1gh", "default_branch": "main", "manifest_path": "guv.yaml" }, "managed_by_manifest": true, "safety": { "grade": "A", "evaluated_at": "2026-10-23T12:00:00Z", "badge_enabled": true, "badge_url": "https://guv.dev/safety/k7q4r8t5v6w3y0z1" }, "created_at": "2026-10-22T09:30:00Z", "updated_at": "2026-10-23T12:00:00Z" } ], "next_cursor": "string", "has_more": false}Create a project
Section titled “Create a project”POST /v1/workspaces/{workspace}/projects- Operation:
createProject - Scopes:
projects:write - Idempotency-Key: required; supports
?dry_run=true
Parameters
| Name | In | Type | Description |
|---|---|---|---|
workspace * |
path | string | Workspace id (ws_…) or slug. |
Request body (application/json, required): ProjectCreate
| Field | Type | Description |
|---|---|---|
name * |
string | |
slug |
string | |
description |
string | |
region |
Region | |
residency |
Residency | |
classification |
Classification | |
repo |
RepoBinding | |
environments |
array of string | Environments to create. Defaults to [production]. Default ["production"]. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
Dry run. Nothing was changed; this is what would happen. | Plan |
201 Created |
Created. | Project |
Errors: 400, 401, 402, 403, 404, 409, 422, 429, 500, as problem+json with a stable code.
Example
curl -X POST https://api.guv.dev/v1/workspaces/acme/projects \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "slug": "string", "description": "string", "region": "lon1", "residency": "uk-only", "classification": "standard", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "string", "default_branch": "main", "manifest_path": "guv.yaml" }, "environments": [ "production" ]}'Example response
{ "id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "slug": "primecarers", "name": "Primecarers", "description": "Care management platform", "region": "lon1", "residency": "uk-only", "classification": "special-category", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "ghi_01jbx2m9k7q4r8t5v6w3y0z1gh", "default_branch": "main", "manifest_path": "guv.yaml" }, "managed_by_manifest": true, "safety": { "grade": "A", "evaluated_at": "2026-10-23T12:00:00Z", "badge_enabled": true, "badge_url": "https://guv.dev/safety/k7q4r8t5v6w3y0z1" }, "created_at": "2026-10-22T09:30:00Z", "updated_at": "2026-10-23T12:00:00Z"}Get a project
Section titled “Get a project”GET /v1/projects/{project}- Operation:
getProject - Scopes:
projects: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). |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. | Project |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/projects/shopfront \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "slug": "primecarers", "name": "Primecarers", "description": "Care management platform", "region": "lon1", "residency": "uk-only", "classification": "special-category", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "ghi_01jbx2m9k7q4r8t5v6w3y0z1gh", "default_branch": "main", "manifest_path": "guv.yaml" }, "managed_by_manifest": true, "safety": { "grade": "A", "evaluated_at": "2026-10-23T12:00:00Z", "badge_enabled": true, "badge_url": "https://guv.dev/safety/k7q4r8t5v6w3y0z1" }, "created_at": "2026-10-22T09:30:00Z", "updated_at": "2026-10-23T12:00:00Z"}Update a project
Section titled “Update a project”PATCH /v1/projects/{project}- Operation:
updateProject - Scopes:
projects:write - Idempotency-Key: required; supports
?dry_run=true - Concurrency: send the
ETagyou read asIf-Match
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
Request body (application/json, required): ProjectUpdate
| Field | Type | Description |
|---|---|---|
name |
string | |
slug |
string | |
description |
string or null | |
residency |
Residency | |
classification |
Classification | |
repo |
RepoBinding or any or null | |
safety_badge_enabled |
boolean |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. With dry_run=true: a Plan (application/vnd.guv.plan+json) and nothing changes. |
Project |
Errors: 400, 401, 403, 404, 409, 412, 422, 429, 500, as problem+json with a stable code.
Example
curl -X PATCH https://api.guv.dev/v1/projects/shopfront \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "name": "string", "slug": "string", "description": "string", "residency": "uk-only", "classification": "standard", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "string", "default_branch": "main", "manifest_path": "guv.yaml" }, "safety_badge_enabled": false}'Example response
{ "id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "slug": "primecarers", "name": "Primecarers", "description": "Care management platform", "region": "lon1", "residency": "uk-only", "classification": "special-category", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "ghi_01jbx2m9k7q4r8t5v6w3y0z1gh", "default_branch": "main", "manifest_path": "guv.yaml" }, "managed_by_manifest": true, "safety": { "grade": "A", "evaluated_at": "2026-10-23T12:00:00Z", "badge_enabled": true, "badge_url": "https://guv.dev/safety/k7q4r8t5v6w3y0z1" }, "created_at": "2026-10-22T09:30:00Z", "updated_at": "2026-10-23T12:00:00Z"}Delete a project
Section titled “Delete a project”DELETE /v1/projects/{project}Stops all services. Databases and volumes are soft-deleted for 7 days, then their backups follow plan retention and are crypto-shredded. Requires confirm=<slug>; agents always need approval.
- Operation:
deleteProject - Scopes:
projects:delete - Approval: Always needs a human approval with a passkey, whoever asks, including agents with full scopes.
- Idempotency-Key: required; supports
?dry_run=true - Concurrency: send the
ETagyou read asIf-Match
Parameters
| Name | In | Type | Description |
|---|---|---|---|
project * |
path | string | Project id (prj_…) or slug (resolved in the Guv-Workspace workspace, or the caller’s default). |
confirm |
query | string | Typed confirmation for destructive actions. Must equal the resource name (or slug/hostname). Missing or wrong values return confirmation_required. |
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, 412, 422, 429, 500, as problem+json with a stable code.
Example
curl -X DELETE https://api.guv.dev/v1/projects/shopfront \ -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" }}Everything about a project in one call
Section titled “Everything about a project in one call”GET /v1/projects/{project}/status- Operation:
getProjectStatus - Scopes:
projects:read - CLI:
guv status
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 |
|---|---|---|
200 OK |
OK. | ProjectStatus |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/projects/shopfront/status \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "project": { "id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "workspace_id": "ws_01jbx2m9k7q4r8t5v6w3y0z1ws", "slug": "primecarers", "name": "Primecarers", "description": "Care management platform", "region": "lon1", "residency": "uk-only", "classification": "special-category", "repo": { "provider": "github", "full_name": "primecarers/platform", "installation_id": "ghi_01jbx2m9k7q4r8t5v6w3y0z1gh", "default_branch": "main", "manifest_path": "guv.yaml" }, "managed_by_manifest": true, "safety": { "grade": "A", "evaluated_at": "2026-10-23T12:00:00Z", "badge_enabled": true, "badge_url": "https://guv.dev/safety/k7q4r8t5v6w3y0z1" }, "created_at": "2026-10-22T09:30:00Z", "updated_at": "2026-10-23T12:00:00Z" }, "headline": "All 4 services are live. £7.42 spent of your £400 cap this month. Safety grade A.", "environments": [ { "id": "string", "name": "string", "status": "healthy", "services": [ { "id": "string", "name": "string", "kind": "web", "status": "no_deployment", "url": "string", "current_deployment_id": "string", "last_error": "string", "cost_today": { "amount": "7.42", "currency": "GBP", "display": "£7.42" } } ], "databases": [ { "id": "string", "name": "string", "engine": "string", "status": "string", "last_backup_at": "2026-10-22T13:05:00Z" } ] } ], "recent_deployments": [ { "id": "dep_01jbx2m9k7q4r8t5v6w3y0z1dp", "project_id": "prj_01jbx2m9k7q4r8t5v6w3y0z1pr", "environment_id": "env_01jbx2m9k7q4r8t5v6w3y0z1en", "service_id": "svc_01jbx2m9k7q4r8t5v6w3y0z1sv", "service_name": "web", "environment_name": "production", "status": "active", "status_text": "Live since 14:05", "trigger": { "kind": "git_push", "actor": { "type": "github", "id": "stewartwebb", "display": "stewartwebb pushed to main" } }, "source": { "kind": "git", "commit": { "sha": "a1b2c3d4e5f60718293a4b5c6d7e8f9012345678", "branch": "main", "message": "Fix rota export", "author": "Stewart Webb" } }, "build_id": "bld_01jbx2m9k7q4r8t5v6w3y0z1bd", "image": { "digest": "sha256:4b1f0e9d8c7b6a5948372615f4e3d2c1b0a9f8e7d6c5b4a3928170f6e5d4c3b2", "signed": true }, "rollback_of": null, "operation_id": "op_01jbx2m9k7q4r8t5v6w3y0z1op", "url": "https://www.primecarers.co.uk", "failure": null, "created_at": "2026-10-23T14:02:00Z", "started_at": "2026-10-23T14:02:01Z", "active_at": "2026-10-23T14:05:12Z", "finished_at": "2026-10-23T14:05:12Z", "duration_ms": 191000 } ], "spend": { "month_to_date": { "amount": "7.42", "currency": "GBP", "display": "£7.42" }, "forecast_month_end": { "amount": "7.42", "currency": "GBP", "display": "£7.42" }, "cap": { "amount": "7.42", "currency": "GBP", "display": "£7.42" } }, "safety_grade": "A", "pending_approvals": 1, "open_incidents": 1}Get a project’s saved canvas layout
Section titled “Get a project’s saved canvas layout”GET /v1/projects/{project}/canvas- Operation:
getProjectCanvas - Scopes:
projects: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 | Environment name or id; an environment override is returned when present, otherwise the project layout. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. | ProjectCanvas |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/projects/shopfront/canvas \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "project_id": "string", "environment_id": "string", "scope": "project", "canvas": { "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 } }, "etag": "\"1\"", "managed_by_manifest": false, "drift": { "detected": false, "reason": "none", "manifest_canvas": { "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 } } }, "warnings": [ { "path": "/canvas/nodes/services.web", "code": "canvas_node_unknown", "message": "Canvas node is not declared.", "fix": "Remove this canvas node or declare the resource." } ], "source": "api", "updated_at": "2026-10-22T13:05:00Z", "updated_by": { "type": "agent", "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk", "display": "Stewart Webb via agent claude-code-on-stewart-laptop", "via_agent": { "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk", "name": "claude-code-on-stewart-laptop" }, "on_behalf_of": { "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us", "display": "Stewart Webb" } }}Save a project’s canvas layout
Section titled “Save a project’s canvas layout”PUT /v1/projects/{project}/canvas- Operation:
updateProjectCanvas - Scopes:
projects:write - Idempotency-Key: required; supports
?dry_run=true - Concurrency: send the
ETagyou read asIf-Match
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 | Environment name or id for an environment-specific override. |
Request body (application/json, required): CanvasLayout
| Field | Type | Description |
|---|---|---|
layout |
auto, manual |
Default auto. |
direction |
LR, TB |
Default LR. |
nodes |
map of string to CanvasNodePosition | |
groups |
array of CanvasGroup | |
viewport |
CanvasViewport |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
Updated layout, or a Plan when dry_run=true. | ProjectCanvas |
Errors: 400, 401, 403, 404, 409, 412, 422, 429, 500, as problem+json with a stable code.
Example
curl -X PUT https://api.guv.dev/v1/projects/shopfront/canvas \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 }}'Example response
{ "project_id": "string", "environment_id": "string", "scope": "project", "canvas": { "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 } }, "etag": "\"1\"", "managed_by_manifest": false, "drift": { "detected": false, "reason": "none", "manifest_canvas": { "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 } } }, "warnings": [ { "path": "/canvas/nodes/services.web", "code": "canvas_node_unknown", "message": "Canvas node is not declared.", "fix": "Remove this canvas node or declare the resource." } ], "source": "api", "updated_at": "2026-10-22T13:05:00Z", "updated_by": { "type": "agent", "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk", "display": "Stewart Webb via agent claude-code-on-stewart-laptop", "via_agent": { "id": "tok_01jbx2m9k7q4r8t5v6w3y0z1tk", "name": "claude-code-on-stewart-laptop" }, "on_behalf_of": { "id": "usr_01jbx2m9k7q4r8t5v6w3y0z1us", "display": "Stewart Webb" } }}Canvas graph for one environment
Section titled “Canvas graph for one environment”GET /v1/projects/{project}/environments/{environment}/graph- Operation:
getProjectGraph - Scopes:
projects: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 * |
path | string | Environment id (env_…) or name. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 OK |
OK. | ProjectGraph |
Errors: 400, 401, 403, 404, 429, 500, as problem+json with a stable code.
Example
curl https://api.guv.dev/v1/projects/shopfront/environments/production/graph \ -H "Authorization: Bearer $GUV_TOKEN"Example response
{ "environment_id": "string", "nodes": [ { "id": "string", "type": "service", "name": "string", "status": "string", "position": { "x": 0.5, "y": 0.5 }, "data": {} } ], "edges": [ { "id": "string", "from": "string", "to": "string", "kind": "reference", "observed": false, "label": "string" } ], "canvas": { "layout": "manual", "direction": "LR", "nodes": { "services.web": { "x": 0, "y": 0 } }, "viewport": { "x": 0, "y": 0, "zoom": 1 } }}Save canvas node positions
Section titled “Save canvas node positions”PATCH /v1/projects/{project}/environments/{environment}/graph/layout- Operation:
updateProjectGraphLayout - Scopes:
projects:read - Idempotency-Key: required
- Concurrency: send the
ETagyou read asIf-Match
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 * |
path | string | Environment id (env_…) or name. |
Request body (application/json, required): GraphLayoutUpdate
| Field | Type | Description |
|---|---|---|
positions * |
map of string to object | Node id to canvas position. |
Responses
| Status | Meaning | Body |
|---|---|---|
204 No Content |
Done. No body. | none |
Errors: 400, 401, 403, 404, 409, 412, 422, 429, 500, as problem+json with a stable code.
Example
curl -X PATCH https://api.guv.dev/v1/projects/shopfront/environments/production/graph/layout \ -H "Authorization: Bearer $GUV_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "positions": { "key": { "x": 0.5, "y": 0.5 } }}'