---
title: Policies
path: reference/policies
status: published
---

# Policies Reference

Endpoint reference for access policies. For the guide, see [Policies](../api-guides/policies). For the model, see [Policies and Permissions](../core-concepts/policies-and-permissions).

**Base path:** `/v1/policies/`

## GET /v1/policies

List policies.

Query: `scope` (`tenant`|`partner`), `include_inactive`, `limit`, `cursor`.

Response:

```json
{
  "policies": [
    {
      "id": "pol_abc",
      "name": "production-read-only",
      "rule_count": 2,
      "binding_count": 5,
      "is_active": true,
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-15T00:00:00Z"
    }
  ],
  "cursor": null,
  "has_more": false
}
```

**Scope:** `policies:read`.

## POST /v1/policies

Create.

Body:

| Field | Type | Required |
|-------|------|----------|
| `name` | string | Yes |
| `description` | string | No |
| `rules` | array | Yes |
| `rules[].path_pattern` | string | Yes |
| `rules[].permissions` | array | Yes |
| `rules[].conditions` | object | No |
| `scope` | `tenant`\|`partner` | No, default `tenant` |

Response `201 Created`: full policy object.

**Scope:** `policies:write`.

## GET /v1/policies/{id}

Get one policy, including rules and bindings.

**Scope:** `policies:read`.

## PUT /v1/policies/{id}

Replace policy (rules + metadata). Bindings preserved.

**Scope:** `policies:write`.

## PATCH /v1/policies/{id}

Partial update (only provided fields).

**Scope:** `policies:write`.

## DELETE /v1/policies/{id}

Delete. Returns `409 policy_in_use` if bindings exist. Add `?force=true` to remove them.

**Scope:** `policies:write`.

## GET /v1/policies/{id}/bindings

List bindings for a policy.

**Scope:** `policies:read`.

## POST /v1/policies/{id}/bindings

Create binding.

Body:

| Field | Description |
|-------|-------------|
| `identity_type` | `user`, `service_account`, `group` |
| `identity_id` | ScaiKey identity ID |
| `expires_at` | Optional auto-expiry |

Response `201 Created`.

**Scope:** `policies:write`.

## DELETE /v1/policies/{id}/bindings/{binding_id}

Remove binding.

**Scope:** `policies:write`.

## POST /v1/policies/test

Dry-run access check. Does not write audit.

Body:

| Field | Description |
|-------|-------------|
| `identity_id` | Who to check |
| `path` | Target path |
| `permission` | `read`, `write`, `delete`, `list`, `rotate`, `admin` |
| `context.source_ip` | For IP range conditions |
| `context.mfa_verified` | For MFA conditions |
| `context.timestamp` | For time-window conditions (defaults to now) |

Response:

```json
{
  "allowed": true,
  "matching_policies": [{"id": "pol_abc", "name": "...", "matching_rule_index": 0}],
  "evaluated_at": "2026-04-23T..."
}
```

On deny:

```json
{
  "allowed": false,
  "reason": "condition_failed",
  "failed_condition": "require_mfa",
  "matching_rule": {"policy_id": "pol_abc", "rule_index": 0}
}
```

**Scope:** `policies:read`.

## Permissions

| Permission | What it allows |
|------------|----------------|
| `read` | Read values |
| `write` | Create/update |
| `delete` | Soft/hard delete |
| `list` | Enumerate, read metadata |
| `rotate` | Trigger rotation |
| `admin` | All |

## Conditions

| Key | Description |
|-----|-------------|
| `ip_ranges` | Array of CIDRs |
| `require_mfa` | Boolean; token's MFA must be recent (< 15 min) |
| `time_window.start` / `.end` | `HH:MM` UTC |

## Related

- [Policies](../api-guides/policies)
- [Policies and Permissions](../core-concepts/policies-and-permissions)
