---
title: "Create a new immutable policy set version"
method: POST
path: "/zones/{zone_id}/policy-sets/{policy_set_id}/versions"
tags: ["PolicySetVersions"]
---

# Create a new immutable policy set version

`POST /zones/{zone_id}/policy-sets/{policy_set_id}/versions`

## 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}/versions`
- Auth: one of:
  - HTTP bearer
  - OAuth 2
    - Client credentials
    - Token URL: `/service-account-token`

Validates the manifest, computes SHA, and creates an immutable version snapshot.

## Path parameters

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

## Headers

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

## Request body

- PdpCreatePolicySetVersionRequest
  - `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
  - `schema_version` string, required: Schema version to pin to this policy set version.

## Response `201`

The newly created 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).

## Other responses

- `400`: bad request error response when caller supplied invalid input data
- `401`: unauthorized error response when caller session is not authenticated
- `403`: forbidden error response when caller does not have permissions to a resource
- `404`: not found error response when caller does not have permission to see a resource or the resource does not exist
- `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`: 26 breaking, 8 warning, 83 info
  - the response property `details/items/field` became optional for the status `400`
  - the response property `details/items/field` became optional for the status `401`
  - the response property `details/items/field` became optional for the status `403`
  - the response property `details/items/field` became optional for the status `404`
  - …113 more
- **2026-04-02** `aee6e66de0d3`: 3 breaking, 12 info
  - removed the required property `attestation/payload` from the response with the `201` status
  - removed the required property `attestation/protected` from the response with the `201` status
  - removed the required property `attestation/signature` from the response with the `201` status
  - the endpoint scheme security `OAuth2` was added to the API
  - …11 more
- **2026-03-16** `d3d69f45bfed`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/keycardai/apis/untitled-api/changes/zones/:zone_id/policy-sets/:policy_set_id/versions/post.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)
