Skip to content
Select themeSelect language

`POST /api/proposals/{id}/approve` -- approve + apply + materialize. Returns the applied proposal with `apply_result` describing what landed; records an audit event with the materialized resource id.

POST
/api/proposals/{id}/approve
curl --request POST \
--url https://example.com/api/proposals/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/approve
id
required
string format: uuid
Media typeapplication/json

One proposal on the wire. A presentation view over the row; payload is passed through as-is so the inbox can render the proposed diff.

object
app_id

The target App, surfaced under the frontend’s app_id name (the row stores it as the generic target_id). Only app.file_upsert carries one.

string | null format: uuid
applied_at
string | null format: date-time
apply_result

What an approve materialized ({ kind, resource_id }). Populated on the approve response; null on list/get reads.

created_at
required
string format: date-time
created_by_task

The proposing agent task id (when proposer_kind == "agent"); null for a user-raised proposal.

string | null
expires_at
required
string format: date-time
id
required
string format: uuid
kind
required
string
payload
required
review_note

Reviewer’s reject note. Not persisted on the row (it lives in the audit trail), so it is null on reads.

string | null
reviewed_at
string | null format: date-time
reviewed_by
string | null
status
required
string
title
required

Human one-liner derived from kind + payload (e.g. the script name or “3 files”), so the inbox list reads without parsing the payload itself.

string
workspace_id
required
string format: uuid
Examplegenerated
{
"app_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"applied_at": "2026-04-15T12:00:00Z",
"apply_result": "example",
"created_at": "2026-04-15T12:00:00Z",
"created_by_task": "example",
"expires_at": "2026-04-15T12:00:00Z",
"id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0",
"kind": "example",
"payload": "example",
"review_note": "example",
"reviewed_at": "2026-04-15T12:00:00Z",
"reviewed_by": "example",
"status": "example",
"title": "example",
"workspace_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}

A proposed app-file is stale: its if_match etag no longer matches the current version, so the whole proposal is rejected

Media typeapplication/json

The canonical JSON body of every error response — the single source of truth the frontend binds to. Every AppError serializes as this exact shape, and the generated OpenAPI component ApiErrorBody (with its ErrorCode enum) is what the frontend error schema is generated from, so there is no hand-written error schema on either end.

object
code
required

Machine-readable, stable error code.

string
Allowed values: not_found unauthorized forbidden bad_request unprocessable precondition_failed conflict method_not_allowed rate_limited quota_exceeded database_error docker_error vault_error internal_error
details
One of:
null
error
required

Human-readable message (the server’s English text; the client may localize by code).

string
Example
{
"code": "not_found"
}