Apply the full desired state of a policy set
Changed onDeclarative 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.
Request
- Base URLhttps://docs.keycard.ai (the document declares no server; this is the origin it was published from)
- URLhttps://docs.keycard.ai/zones/{zone_id}/policy-sets/{policy_set_id}/manifest
- Auth
- HTTP bearer
- or OAuth 2Client credentialsToken URL: /service-account-token
Path parameters
The zone identifier
The policy set identifier
Query parameters
Not yet implemented — true returns 501 until ACC-1002. When true, performs validation, schema resolution, and the content diff, and returns the would-be changes and warnings — but persists nothing. policy_set_version is omitted from the response and no ETag is returned.
Headers
ETag value from a previous GET/POST/PATCH response. When provided, the update only succeeds if the resource has not been modified; otherwise 412 Precondition Failed is returned.
API version header (date-based, e.g. 2026-02-01)
Unique request identifier specified by the originating caller and passed along by proxies.
Request body
Response
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.
Changes
- info
endpoint added
- info