---
title: "Create or replace a policy set binding"
method: PUT
path: "/zones/{zone_id}/policy-bindings"
tags: ["PolicyBindings"]
---

# Create or replace a policy set binding

`PUT /zones/{zone_id}/policy-bindings`

Upserts a binding for a slot identified by (scope_type, scope_target_id, mode).
If a binding already exists for the slot, it is replaced. The binding ID
is stable per slot and survives replacements.

## Path parameters

- `zone_id` string, required

## Headers

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

## Request body

- PdpSetPolicySetBindingRequest — Exactly one of `target_type` or the deprecated `scope_type` must be provided; supplying both with different values returns 400. The same applies to `target_id` / `scope_target_id`.
  - `scope_type` 'zone' — **Deprecated.** Use `target_type` instead.
  - `scope_target_id` string — **Deprecated.** Use `target_id` instead.
  - `target_type` 'zone' — What the binding targets. Only `zone` is supported for direct binding writes.
  - `target_id` string — Defaults to zone_id for zone targets. Required for other targets.
  - `mode` 'shadow', required — Binding mode
  - `policy_set_id` string, required — Public ID of the policy set to bind
  - `policy_set_version_id` string, required — Public ID of the policy set version to bind

## Response `200`

The binding was created or replaced

- 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` — 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
- `default` — internal server error response when server encountered error of its own creation

---

[API](https://skmtc.net/keycardai/apis/untitled-api.md) · [All operations](https://skmtc.net/keycardai/apis/untitled-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/keycardai/untitled-api/versions/2f5033ed4491/schema)
