---
title: "Apply the full desired state of a policy set"
method: PUT
path: "/zones/{zone_id}/policy-sets/{policy_set_id}/manifest"
tags: ["PolicySetManifest"]
---

# Apply the full desired state of a policy set

`PUT /zones/{zone_id}/policy-sets/{policy_set_id}/manifest`

## Request

- Base URL: `https://docs.keycard.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://docs.keycard.ai/zones/{zone_id}/policy-sets/{policy_set_id}/manifest`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Client credentials
    - Token URL: `/service-account-token`

Declarative full-replace apply. `entries` is the complete desired
content of the policy set: **any policy omitted from `entries` is
dropped from the resulting version** (its rows are retained and a later
apply may reference it again). Entry order is preserved for newly added
policies; retained policies keep their current manifest order, so an
unchanged apply is byte-identical and dedups to the existing version.

Per entry, the server diffs by content SHA against the version pinned
in the latest manifest: unchanged content under the same schema version
reuses the pinned policy version; changed content mints a new policy
version; `new_policy` entries mint a customer-owned policy with the
requested name. All of this, plus the policy set version mint and the
optional activation, happens in **one transaction** (row lock on the
policy set). Cedar validation runs against the resolved schema before
the transaction opens — an invalid policy rejects the whole apply with
zero writes.

**Schema resolution:** `schema_version` omitted → the latest version's
schema if still writable, else the zone default. Provided →
authoritative (`schema_version_unsupported` if unknown, unattested, or
archived). All entries validate against the single resolved version
(uniform-schema-per-version is retained); a pinned policy version
created under a different schema is re-versioned even if its content is
unchanged.

**Activation:** `activate: true` requires a zone-targeted set and rebinds the
`(zone, target_type=zone, mode=active)` slot to the resulting version
in the same transaction. It requires `policy_set_bindings:activate`
permission and preserves the previous binding in binding history.
Unchanged content returns `created: false` and still activates the
resulting version. An already active version keeps its binding unchanged.

**Concurrency:** `If-Match` is optional (omitted = last-writer-wins).
When provided it is compared, under the row lock, against the digest of
the latest version's manifest; mismatch returns `412` (`etag_mismatch`)
carrying the current `ETag`. The token is content-addressed, not a
version identity: it guards "the content I read is still the content in
place", so two versions with identical manifests are interchangeable for
the precondition, and archiving the tip does not invalidate a token whose
digest the new latest version still carries. Do not re-key it on
`policy_set_version_id`.

**Identity and reverts:** a desired state identical to the **latest**
version is a no-op (`created: false`, latest returned, nothing minted).
A desired state matching an **older** version mints a new version at
the tip — history is linear, never forked. Rolling back deployment
without a content edit is not an apply at all: bind the older version
via `PUT /policy-bindings`.

**Authorization:** the route gate is `update` on the policy set. When
`activate: true`, the handler additionally requires the `activate`
action on `policy_set_bindings`.

Domain error codes: `policy_set_not_found`, `policy_not_found`,
`policy_version_not_found`, `version_archived`,
`schema_version_mismatch`, `manifest_duplicate_policy`,
`missing_cedar_content`, `invalid_cedar`, `schema_version_unsupported`,
`policy_name_conflict`, `policy_archived`, `set_archived`,
`etag_mismatch`, `activate_requires_zone_target`, `shadow_equals_active`.

## Path parameters

- `zone_id` string, required
- `policy_set_id` string, required

## Query parameters

- `dry_run` boolean

## Headers

- `If-Match` string
- `X-API-Version` string
- `X-Client-Request-ID` string, uuid

## Request body

- PdpApplyPolicySetManifestRequest: The full desired state of the policy set's content. Omitted = dropped.
  - `schema_version` string: Cedar schema version to validate and pin the resulting version against. Omitted: the server resolves it (latest version's schema if still writable, else the zone default).
  - `activate` boolean: Also rebind the zone/active slot to the resulting version in the same transaction. Requires the `activate` permission on policy set bindings. Targets only the zone/active slot; other slots go through PUT /policy-bindings.
  - `entries` PdpApplyManifestEntry[], required: Complete desired content. Order is preserved for newly added policies; retained policies keep their current manifest order (so an unchanged apply reproduces the identical manifest and dedups).
    - union
      - PdpExistingPolicyEntry: Reference to an existing (non-archived) policy in the zone — not limited to policies already in this set. With `cedar_raw`/`cedar_json` (mutually exclusive): the server diffs by content SHA; unchanged content under the resolved schema reuses the pinned policy version, changed content mints a new one. Without content ("pin as-is"): reuses the version pinned in the latest manifest, or the policy's latest version when the policy is newly added to this set. Bare pins are re-versioned when the resolved schema differs from the pinned version's schema. With `policy_version_id`: pins exactly that existing version and mints nothing. Mutually exclusive with `cedar_raw`/`cedar_json` (a version is content; 400 when both are supplied). The version must belong to `policy_id`, must not be archived (`version_archived`), and must have been validated against the resolved schema (`schema_version_mismatch`; no re-versioning). Reported as `repinned` when the set already pins a different version of the policy, otherwise `reused`. Platform-owned policies accept bare pins and `policy_version_id` (customers cannot mint versions of those).
        - `policy_id` string, required: Public ID of an existing policy in the zone.
        - `policy_version_id` string: Public ID of an existing version of `policy_id` to pin. Mutually exclusive with cedar_raw and cedar_json.
        - `cedar_raw` string: Cedar policy text. Mutually exclusive with cedar_json.
        - `cedar_json` object: Cedar policy JSON. Mutually exclusive with cedar_raw.
      - PdpNewPolicyEntry: Mints a new customer-owned policy with the requested name (409 `policy_name_conflict` on collision) plus its first version from the supplied content. Exactly one of `cedar_raw`/`cedar_json` is required.
        - `new_policy` object, required
          - `name` string, required
          - `description` string
        - `cedar_raw` string: Cedar policy text. Mutually exclusive with cedar_json.
        - `cedar_json` object: Cedar policy JSON. Mutually exclusive with cedar_raw.

## Response `200`

The apply result. Always `200` — the resulting version may be newly minted (`created: true`), deduped to an existing version, or a pure no-op (`created: false`); the body discriminates so clients never branch on status.

- PdpApplyPolicySetManifestResult
  - `policy_set_version` PdpPolicySetVersion
    - `id` string, required
    - `policy_set_id` string, required
    - `version` integer, required
    - `schema_version` string, required: Schema version pinned to this policy set version. Determines the Cedar schema used for evaluation when activated.
    - `manifest` PdpPolicySetManifest, required
      - `entries` PdpPolicySetManifestEntry[], required
        - `policy_id` string, required
        - `policy_version_id` string, required
        - `sha` string: SHA-256 of the policy version content, populated by the server
    - `manifest_sha` string, required: Hex-encoded SHA-256 of the canonicalized manifest
    - `attestation` PdpAttestationStatement: Decoded content of an Attestation JWS payload. Describes the exact policy set version composition at attestation time. This schema defines what consumers see after base64url-decoding the Attestation.payload field.
      - `v` 1, required: Statement schema version
      - `type` 'policy_set_attestation', required: Statement type discriminator
      - `zone_id` string, required
      - `policy_set_id` string, required
      - `policy_set_version` integer, required
      - `manifest_sha` string, required: SHA-256 of the policy set version manifest. Verifiers MUST check this matches the policy_set_version.manifest_sha to detect attestation/version mismatches.
      - `status` 'created' | 're_signed', required: Event that produced this attestation. "created" is the initial attestation at version creation; "re_signed" is a re-attestation after key rotation (same content, new signature).
      - `attested_at` string, date-time, required
      - `attested_by` string, required
      - `key_id` string, required: Key ID of the signing key used to produce the attestation signature. Matches the "kid" in the JWS protected header.
    - `active` boolean: Whether this policy set version is currently bound with mode='active'. Always populated in responses; clients must treat absence as unknown rather than inferring 'not bound'.
    - `archived_at` string, date-time, nullable: Timestamp when the version was archived. Non-null only for archived versions; null or absent means not archived.
    - `archived_by` string, nullable: Identifier of the actor that archived the version. Null or absent means not archived.
    - `archived_by_user` PdpUser: The organization user behind a `created_by`, `updated_by` or `archived_by` value. Returned only when `expand[]=user` is requested.
      - `id` string, required: Public ID of the user in the organization's platform zone. This is not the same value as the `*_by` field it expands; use it to link to `/zones/{zone_id}/users/{id}`.
      - `zone_id` string, required: Public ID of the organization's platform zone the user belongs to.
      - `email` string, nullable, required: The user's email address, or null when not known.
    - `created_at` string, date-time, required
    - `created_by` string, required
    - `created_by_user` PdpUser: The organization user behind a `created_by`, `updated_by` or `archived_by` value. Returned only when `expand[]=user` is requested.
      - `id` string, required: Public ID of the user in the organization's platform zone. This is not the same value as the `*_by` field it expands; use it to link to `/zones/{zone_id}/users/{id}`.
      - `zone_id` string, required: Public ID of the organization's platform zone the user belongs to.
      - `email` string, nullable, required: The user's email address, or null when not known.
    - `owner_type` 'platform' | 'customer', required: Who manages this policy set version: - `"platform"` — managed by the Keycard platform (system policy set versions). - `"customer"` — managed by the tenant (custom policy set versions).
  - `created` boolean, required: True when the server mints a new policy set version. False when the content matches the latest version, even when activate changes the active binding. Reverts to older content mint a new version at the tip and return true.
  - `changes` PdpApplyChange[], required: Per-policy outcome of the diff.
    - `policy_id` string, required
    - `name` string, required: The policy's name. Lets a caller correlate a `created_policy` row with its `new_policy` request entry without a re-list.
    - `action` 'created_policy' | 'created_version' | 'reused' | 'repinned' | 'dropped', required: `repinned`: an explicit `policy_version_id` replaced a different version the set already pinned for that policy; no version minted.
    - `policy_version_id` string: Absent when action is dropped.
  - `warnings` PdpApplyWarning[]: Non-fatal findings — e.g. Cedar actions unknown to the resolved schema. Present only when non-empty.
    - `code` string, required: Machine-readable warning code, e.g. unknown_actions.
    - `message` string, required
  - `binding` PdpPolicySetBinding
    - `id` string, required: Binding identifier (stable per slot)
    - `scope_type` 'zone', required: **Deprecated.** Use `target_type` instead. Carries the same value.
    - `scope_target_id` string, required: **Deprecated.** Use `target_id` instead. Carries the same value.
    - `target_type` 'zone' | 'user', required: What this binding targets
    - `target_id` string, required: Target entity ID. Equals zone_id for zone-targeted bindings.
    - `mode` 'active' | 'shadow', required: Binding mode
    - `policy_set_id` string, required: Public ID of the bound policy set
    - `policy_set_version_id` string, required: Public ID of the bound policy set version
    - `created_at` string, date-time, required

## Other responses

- `400`: bad request error response when caller supplied invalid input data
- `401`: unauthorized error response when caller session is not authenticated
- `403`: Caller lacks the route permission, or `activate: true` and the caller lacks the `activate` permission on policy set bindings — the error names the missing permission.
- `404`: not found error response when caller does not have permission to see a resource or the resource does not exist
- `409`: Conflict: `policy_name_conflict` (a `new_policy` name already exists in the zone), `policy_archived`, `set_archived`.
- `412`: Precondition failed (`etag_mismatch`): the latest manifest no longer matches `If-Match`. The current `ETag` is returned so the client can re-read, merge, and retry.
- `429`: rate limit exceeded error response when caller has exhausted api limits for the given time period
- `500`: internal server error response when server encountered error of its own creation
- `503`: service unavailable error when server you're attempting to reach is not available
- `default`: internal server error response when server encountered error of its own creation

## Changes

- **2026-10-16** `0cfaff865771`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/keycardai/apis/untitled-api/changes/zones/:zone_id/policy-sets/:policy_set_id/manifest/put.md)

---

[API](https://skmtc.dev/keycardai/apis/untitled-api.md) - [All operations](https://skmtc.dev/keycardai/apis/untitled-api/llms.txt) - [OpenAPI document](https://skmtc.dev/keycardai/apis/untitled-api/revisions/0dee67e0a73a?raw)
