Skip to content

`GET /v1/orgs/{org}/proposals/{proposal_id}`.

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

Organization slug.

proposal_id
required
string

The prp_… id.

The proposal in full. ⚠️ changes is grouped by package and both versions, not one entry per host per package — a fleet on two starting versions is two changes.

Media typeapplication/json
object
approvals
required
integer format: int64
auto_merge
required
boolean
closed_at
string | null format: date-time
created_at
required
string format: date-time
hosts
required
integer format: int64
id
required
string
kind
required
string
known_exploited
required

Something it fixes is on CISA’s Known Exploited Vulnerabilities list (DAWG-72). Shown beside the severity; it never raises it.

boolean
max_severity

null means nothing in this proposal was rated, which is not the same as harmless.

string | null
number
required

The #142 people say out loud.

integer format: int32
outstanding
required

Zero for anything final, whatever the counts say — see [db::inbox::Summary::outstanding].

integer format: int64
packages
required

Distinct packages, not proposal_items rows.

integer format: int64
required
required
integer format: int64
scheduled_for
string | null format: date-time
snoozed_until
string | null format: date-time
status
required

A status this build does not recognise is passed through as stored rather than hidden or guessed at. A portal should render an unknown status as unknown — it means this API is older than the thing that wrote the row, which is a deployment fact worth seeing.

string
title
required
string
updated_at
required
string format: date-time
changes
required

What changes, grouped by package and by the versions involved — not one entry per host per package, which on a real fleet is tens of thousands of them. See db::inbox.

Array<object>
object
advisories
required
Array<object>
object
cves
required
Array<string>
external_id
required
string
id
required
string
known_exploited
required

A CVE it lists is on CISA’s Known Exploited Vulnerabilities list (DAWG-72).

boolean
severity
string | null
source
required
string
title
required
string
url
string | null
changelog
One of:

What the archive’s changelog says changed between the two versions (DAWG-71). Absent where there is none to fetch: the RHEL family, or an agent too old to report the source package.

object
entries
required

After the installed version, up to and including the offered one, newest first; at most 15.

Array<object>
object
changes
required

The entry’s lines as the maintainer wrote them.

string
date
required
string
distribution
required

trixie-security, jammy-updates.

string
maintainer
required
string
urgency
required
string
version
required
string
status
required

found; pending, not fetched yet; or missing, the archive has none for this version.

string
url
string | null
ecosystem
required
string
from_version
required
string
hosts
required

How many of the proposal’s hosts make this change. Lower than the proposal’s host count whenever the fleet is not uniform.

integer format: int64
package
required
string
to_version
required
string
decisions
required
Array<object>
object
at
required
string format: date-time
by
One of:

⚠️ Absent means a policy auto-merge, not an unknown person. A portal that renders this as “somebody” would be attributing a machine decision to a human it cannot name.

object
email
required
string
id
required
string
name
string | null
decision
required
string
reason
string | null
policy
One of:
object
id
required
string
name

Absent if the policy has since been hard-deleted.

string | null
version
required

The version this proposal was generated under, which is very possibly not the current one. That is the point of storing it.

integer format: int32
reboot
required

What approving this lets it do about reboots (DAWG-103), as the rule said when the proposal was opened: never, immediate (reboot hosts that need it as part of the job) or in_window (the same, only inside the maintenance window). An agent reboots only if its own agent.toml permits it.

string
release_upgrade
One of:

A release upgrade’s from and to (kind dist_upgrade, DAWG-153). Absent on a package change, which says what it changes in changes.

object
distro
required

As distro on a host: debian, ubuntu.

string
from
required
object
codename
string | null
version
required

13, 24.04.

string
to
required
object
codename
string | null
version
required

13, 24.04.

string
superseded_by
string | null
targets
required

⚠️ targets, not hosts. This struct flattens [SummaryBody], which already has a hosts — a count. Naming the list hosts too made the array silently shadow the number, so the same key was an integer on the list endpoint and an array on this one. Nothing failed: serde flattening has a last-one-wins rule and is happy to apply it. A generated client cannot express that, and a portal reading proposal.hosts would get whichever it was last given.

Found by running scripts/try-proposals.sh and reading the output, which is the only place a key that collides with itself is visible.

Array<object>
object
display_name
string | null
hostname
required
string
id
required
string
targets_complete
required

Whether targets is all of them. The count in hosts is always exact; this list is capped.

boolean
Examplegenerated
{
"approvals": 1,
"auto_merge": true,
"closed_at": "2026-04-15T12:00:00Z",
"created_at": "2026-04-15T12:00:00Z",
"hosts": 1,
"id": "example",
"kind": "example",
"known_exploited": true,
"max_severity": "example",
"number": 1,
"outstanding": 1,
"packages": 1,
"required": 1,
"scheduled_for": "2026-04-15T12:00:00Z",
"snoozed_until": "2026-04-15T12:00:00Z",
"status": "example",
"title": "example",
"updated_at": "2026-04-15T12:00:00Z",
"changes": [
{
"advisories": [
{
"cves": [
"example"
],
"external_id": "example",
"id": "example",
"known_exploited": true,
"severity": "example",
"source": "example",
"title": "example",
"url": "example"
}
],
"changelog": {
"entries": [
{
"changes": "example",
"date": "example",
"distribution": "example",
"maintainer": "example",
"urgency": "example",
"version": "example"
}
],
"status": "example",
"url": "example"
},
"ecosystem": "example",
"from_version": "example",
"hosts": 1,
"package": "example",
"to_version": "example"
}
],
"decisions": [
{
"at": "2026-04-15T12:00:00Z",
"by": {
"email": "example",
"id": "example",
"name": "example"
},
"decision": "example",
"reason": "example"
}
],
"policy": {
"id": "example",
"name": "example",
"version": 1
},
"reboot": "example",
"release_upgrade": {
"distro": "example",
"from": {
"codename": "example",
"version": "example"
},
"to": {
"codename": "example",
"version": "example"
}
},
"superseded_by": "example",
"targets": [
{
"display_name": "example",
"hostname": "example",
"id": "example"
}
],
"targets_complete": true
}

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"
}

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"
}

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"
}