Skip to content

`POST /v1/orgs/{org}/proposals/{proposal_id}/approve`.

POST
/v1/orgs/{org}/proposals/{proposal_id}/approve
curl --request POST \
--url https://api.updawg.net/v1/orgs/example/proposals/example/approve

Takes no body. The design document has this endpoint carrying an optional schedule and rollout override; the rollout override belongs to epic 09 and has nowhere to go today, and inventing a field that is accepted and ignored is worse than not having one.

The schedule half arrived with DAWG-92 and is not a field either, because it is not the approver’s to choose: a proposal whose rule says apply: asap_in_window and whose window is shut comes back scheduled, with scheduled_for saying when it opens. Approving is agreeing to the change, not to the hour.

org
required
string

Organization slug.

proposal_id
required
string

The prp_… id.

Where it is now. scheduled with a scheduled_for when the rule wants a maintenance window — approving is agreeing to the change, not to the hour.

Media typeapplication/json

The body every one of the three answers with.

Named for what it is rather than for the endpoint, because all three return it and a portal that has just clicked a button wants the same three facts whichever button it was: where the proposal is now, and how the approval count stands.

object
approvals
required

Distinct people who have approved. A rejected proposal keeps the count it had: it is a record of who had agreed before somebody said no.

integer format: int64
id
required
string
outstanding
required

How many more approvals this needs before it can go ahead.

Zero once there are enough, and zero once there is nothing left to go ahead with — see [ok].

integer format: int64
required
required

What the policy asked for, via proposals.required_approvals.

integer format: int64
scheduled_for

When a proposal that is scheduled may start (DAWG-92).

Present only on that status, and omitted rather than null everywhere else: it is not “unknown”, it is “does not apply”, and a portal that renders a date field will render something for a null.

string | null format: date-time
status
required
string
Examplegenerated
{
"approvals": 1,
"id": "example",
"outstanding": 1,
"required": 1,
"scheduled_for": "2026-04-15T12:00:00Z",
"status": "example"
}

No session.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Not permitted for this role.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

No such proposal here.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Already approved by you, no longer yours to decide, or its current preflight fails on a host (preflight-failed, DAWG-153), or it is a release upgrade no agent carries out yet: anything but Debian (not-executable).

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}

Over the organization’s request limit. Retry-After says when to try again; RateLimit-Limit is the burst.

Media typeapplication/json
object
detail
string | null
status
required
integer format: int32
title
required
string
type
required
string
Examplegenerated
{
"detail": "example",
"status": 1,
"title": "example",
"type": "example"
}