> For the complete documentation index, see [llms.txt](https://docs.p0.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.p0.dev/access-management/just-in-time-access/just-in-time-api/access-policies-api.md).

# Access policies API

The Access Policies API gives you programmatic control over how just-in-time access requests are evaluated and approved. Each **access policy** is a rule that matches on the **requestor** (who is asking) and the **resource** (what they want), then routes matching requests down an **approval** path — manual approval, auto-approval for on-call engineers, standing access, or an outright deny.

Use this API to manage policies as code: read your current rules, add or change a single rule by name, or replace your whole configuration in one request. For the product concepts, evaluation order, and the full filter reference, see the [Access Policies](/access-management/just-in-time-access/access-policies.md) guide.

## Base URL and authentication

Every endpoint lives under your organization's base URL, which includes your organization slug (`orgId`):

```
https://api.p0.app/o/{orgId}
```

All requests require a bearer token. See [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md) to get one — from a Google Cloud service-account token (recommended), the P0 CLI, or a legacy API key — and pass it in the `Authorization` header:

```bash
curl -H "Authorization: Bearer $P0_API_TOKEN" \
  https://api.p0.app/o/{orgId}/policy
```

For the base URL, authentication, and permissions shared by all P0 APIs, see the [P0 API overview](/getting-started/p0-api-overview.md).

## Permissions

| Action                                    | Required permission     | Roles that have it |
| ----------------------------------------- | ----------------------- | ------------------ |
| Read policies (`GET`)                     | `policies.version.read` | Owner, Manager     |
| Change policies (`POST`, `PUT`, `DELETE`) | `policies.version.add`  | Owner              |

A token's privileges come from the P0 role of its identity, so grant each identity the least privilege it needs. A legacy API key carries the full **Owner** role and can call every endpoint on this page. A caller who is authenticated but lacks the permission — for example, a Manager calling a write endpoint — gets `403 Forbidden`, not `401`.

## Two ways to manage policies

Your organization has one active policy configuration — a `PolicyConfig`, which is an object with a `rules` array of individual `Policy` rules. This API exposes that configuration two ways:

* **A single rule at a time**, addressed by its `name`, under `/policy/name/{name}`. Use `POST`, `PUT`, and `DELETE` to add, change, or remove one rule without touching the rest. This is the most direct way to automate incremental changes.
* **The whole configuration at once**, under `/policy`. `POST` replaces the entire rule set. Use this for config-as-code workflows that manage all rules together.

The two shapes carry different bodies: the `/policy/name/{name}` endpoints send and receive a single `Policy`, while `POST /policy` sends a `PolicyConfig` wrapped with a `currentVersion` for [optimistic concurrency](#replace-the-whole-configuration).

P0 keeps a version history of the configuration. **Every write — whether through `/policy/name/{name}` or `POST /policy` — creates a new configuration version.** A single-rule write still versions the whole configuration; it just leaves the other rules unchanged. To read the active configuration and the `version` you need for optimistic concurrency, get [`latest`](#read-the-active-configuration).

Rule names match **case-insensitively**. Creating `Engineering-AWS` when `engineering-aws` already exists returns `409 Conflict`, and a `GET` by either casing finds the same rule.

## Endpoints

## List stored policy configurations

> Returns every document in the organization's policy store, each with its rules and metadata. The result is unordered, and it includes more than version snapshots: the active-configuration pointer is returned with \`id: "latest"\` (and carries a \`version\`), and an options document is returned with \`id: "\_default\_"\` and no \`rules\`. To read the active configuration and its version, get \`latest\` instead (\`GET /policy/latest\`).

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"schemas":{"PolicyConfigList":{"type":"object","properties":{"policies":{"type":"array","items":{"$ref":"#/components/schemas/PolicyConfigWithMeta"}}}},"PolicyConfigWithMeta":{"allOf":[{"$ref":"#/components/schemas/PolicyConfig"},{"type":"object","description":"A stored policy configuration document, with its metadata.","properties":{"id":{"type":"string","description":"The document ID. For a version document this is its auto-generated version ID; for the active-configuration pointer it is the literal `latest`."},"version":{"type":"string","description":"The active configuration's version ID. Present when you read `latest`; use it as `currentVersion` when replacing the whole configuration."},"createdDate":{"type":"string","format":"date-time","description":"When this configuration document was last written."}}}]},"PolicyConfig":{"type":"object","description":"A complete policy configuration — the full set of rules that is active at once.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}},"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}},"responses":{"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."}}},"paths":{"/policy":{"get":{"summary":"List stored policy configurations","description":"Returns every document in the organization's policy store, each with its rules and metadata. The result is unordered, and it includes more than version snapshots: the active-configuration pointer is returned with `id: \"latest\"` (and carries a `version`), and an options document is returned with `id: \"_default_\"` and no `rules`. To read the active configuration and its version, get `latest` instead (`GET /policy/latest`).","responses":{"200":{"description":"The documents in the organization's policy store.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyConfigList"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"}}}}}}
```

## Replace the policy configuration

> Replaces the entire active policy configuration with a new set of rules and creates a new version. Use \`currentVersion\` for optimistic concurrency: pass the \`version\` from \`GET /policy/latest\` (or from a prior write's response), and the request fails with 409 if the configuration changed in the meantime.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"schemas":{"PolicyConfigUpdate":{"type":"object","description":"A request to replace the active policy configuration.","required":["currentVersion","policy"],"properties":{"currentVersion":{"type":"string","description":"The `version` of the configuration you are editing. If it no longer matches the active configuration, the request fails with 409 so you can reload and reapply your edits."},"policy":{"$ref":"#/components/schemas/PolicyConfig"}}},"PolicyConfig":{"type":"object","description":"A complete policy configuration — the full set of rules that is active at once.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}},"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"PolicyConfigVersionResponse":{"type":"object","properties":{"policy":{"$ref":"#/components/schemas/PolicyConfigVersion"}}},"PolicyConfigVersion":{"allOf":[{"$ref":"#/components/schemas/PolicyConfig"},{"type":"object","properties":{"version":{"type":"string","description":"The ID of the newly created configuration version."}}}]}},"responses":{"BadRequestError":{"description":"The request body failed validation — for example, a `name` that contains a slash, a `name` that does not match the URL, an unknown `service`, a filter with a non-string `pattern` or an invalid `key`, or any unknown field (policy objects reject unknown properties). The response body describes the problem."},"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"ConflictError":{"description":"The change conflicts with the current state — for example, a rule with the given name already exists, more than one rule matches the given name, or the supplied `currentVersion` no longer matches the active configuration."},"UnprocessableEntityError":{"description":"A filter `pattern` (or an agentic `subjectPattern`) is not a valid regular expression, or is unsafe because it is subject to catastrophic backtracking."}}},"paths":{"/policy":{"post":{"summary":"Replace the policy configuration","description":"Replaces the entire active policy configuration with a new set of rules and creates a new version. Use `currentVersion` for optimistic concurrency: pass the `version` from `GET /policy/latest` (or from a prior write's response), and the request fails with 409 if the configuration changed in the meantime.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyConfigUpdate"}}}},"responses":{"200":{"description":"The new policy configuration, including its version ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyConfigVersionResponse"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"409":{"$ref":"#/components/responses/ConflictError"},"422":{"$ref":"#/components/responses/UnprocessableEntityError"}}}}}}
```

## Get a policy configuration

> Returns a stored policy configuration by its ID. Pass a version ID to read that snapshot, or the literal \`latest\` to read the active configuration together with its \`version\`.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"parameters":{"policyId":{"name":"policyId","in":"path","required":true,"description":"A policy configuration version ID, returned as `version` by the create endpoint. Pass the literal `latest` to read the active configuration together with its `version` — this is the only way to read the active configuration and the version you need for optimistic concurrency.","schema":{"type":"string"}}},"schemas":{"PolicyConfigResponse":{"type":"object","properties":{"policy":{"$ref":"#/components/schemas/PolicyConfigWithMeta"}}},"PolicyConfigWithMeta":{"allOf":[{"$ref":"#/components/schemas/PolicyConfig"},{"type":"object","description":"A stored policy configuration document, with its metadata.","properties":{"id":{"type":"string","description":"The document ID. For a version document this is its auto-generated version ID; for the active-configuration pointer it is the literal `latest`."},"version":{"type":"string","description":"The active configuration's version ID. Present when you read `latest`; use it as `currentVersion` when replacing the whole configuration."},"createdDate":{"type":"string","format":"date-time","description":"When this configuration document was last written."}}}]},"PolicyConfig":{"type":"object","description":"A complete policy configuration — the full set of rules that is active at once.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}},"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}},"responses":{"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"NotFoundError":{"description":"No policy matches the given name or version ID."}}},"paths":{"/policy/{policyId}":{"get":{"summary":"Get a policy configuration","description":"Returns a stored policy configuration by its ID. Pass a version ID to read that snapshot, or the literal `latest` to read the active configuration together with its `version`.","parameters":[{"$ref":"#/components/parameters/policyId"}],"responses":{"200":{"description":"The requested policy configuration.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PolicyConfigResponse"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"}}}}}}
```

## Get a policy rule

> Returns a single policy rule from the active configuration by its name.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"parameters":{"name":{"name":"name","in":"path","required":true,"description":"The policy rule's unique name. Cannot contain a forward slash (`/`) or backslash (`\\`). Names match case-insensitively, so `Engineering-AWS` and `engineering-aws` refer to the same rule.","schema":{"type":"string"}}},"schemas":{"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}},"responses":{"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"NotFoundError":{"description":"No policy matches the given name or version ID."},"ConflictError":{"description":"The change conflicts with the current state — for example, a rule with the given name already exists, more than one rule matches the given name, or the supplied `currentVersion` no longer matches the active configuration."}}},"paths":{"/policy/name/{name}":{"get":{"summary":"Get a policy rule","description":"Returns a single policy rule from the active configuration by its name.","parameters":[{"$ref":"#/components/parameters/name"}],"responses":{"200":{"description":"The requested policy rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"},"409":{"$ref":"#/components/responses/ConflictError"}}}}}}
```

## Create a policy rule

> Adds a single named rule to the active configuration and creates a new configuration version. The \`name\` in the body must match the \`name\` in the path. Fails with 409 if a rule with that name already exists — use \`PUT\` to change an existing rule.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"parameters":{"name":{"name":"name","in":"path","required":true,"description":"The policy rule's unique name. Cannot contain a forward slash (`/`) or backslash (`\\`). Names match case-insensitively, so `Engineering-AWS` and `engineering-aws` refer to the same rule.","schema":{"type":"string"}}},"schemas":{"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}},"responses":{"BadRequestError":{"description":"The request body failed validation — for example, a `name` that contains a slash, a `name` that does not match the URL, an unknown `service`, a filter with a non-string `pattern` or an invalid `key`, or any unknown field (policy objects reject unknown properties). The response body describes the problem."},"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"ConflictError":{"description":"The change conflicts with the current state — for example, a rule with the given name already exists, more than one rule matches the given name, or the supplied `currentVersion` no longer matches the active configuration."},"UnprocessableEntityError":{"description":"A filter `pattern` (or an agentic `subjectPattern`) is not a valid regular expression, or is unsafe because it is subject to catastrophic backtracking."}}},"paths":{"/policy/name/{name}":{"post":{"summary":"Create a policy rule","description":"Adds a single named rule to the active configuration and creates a new configuration version. The `name` in the body must match the `name` in the path. Fails with 409 if a rule with that name already exists — use `PUT` to change an existing rule.","parameters":[{"$ref":"#/components/parameters/name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"responses":{"201":{"description":"The created policy rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"409":{"$ref":"#/components/responses/ConflictError"},"422":{"$ref":"#/components/responses/UnprocessableEntityError"}}}}}}
```

## Update a policy rule

> Replaces an existing named rule in the active configuration and creates a new configuration version. The \`name\` in the body must match the \`name\` in the path. Fails with 404 if no rule with that name exists — use \`POST\` to create one.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"parameters":{"name":{"name":"name","in":"path","required":true,"description":"The policy rule's unique name. Cannot contain a forward slash (`/`) or backslash (`\\`). Names match case-insensitively, so `Engineering-AWS` and `engineering-aws` refer to the same rule.","schema":{"type":"string"}}},"schemas":{"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}},"responses":{"BadRequestError":{"description":"The request body failed validation — for example, a `name` that contains a slash, a `name` that does not match the URL, an unknown `service`, a filter with a non-string `pattern` or an invalid `key`, or any unknown field (policy objects reject unknown properties). The response body describes the problem."},"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"NotFoundError":{"description":"No policy matches the given name or version ID."},"ConflictError":{"description":"The change conflicts with the current state — for example, a rule with the given name already exists, more than one rule matches the given name, or the supplied `currentVersion` no longer matches the active configuration."},"UnprocessableEntityError":{"description":"A filter `pattern` (or an agentic `subjectPattern`) is not a valid regular expression, or is unsafe because it is subject to catastrophic backtracking."}}},"paths":{"/policy/name/{name}":{"put":{"summary":"Update a policy rule","description":"Replaces an existing named rule in the active configuration and creates a new configuration version. The `name` in the body must match the `name` in the path. Fails with 404 if no rule with that name exists — use `POST` to create one.","parameters":[{"$ref":"#/components/parameters/name"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"responses":{"200":{"description":"The updated policy rule.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Policy"}}}},"400":{"$ref":"#/components/responses/BadRequestError"},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"},"409":{"$ref":"#/components/responses/ConflictError"},"422":{"$ref":"#/components/responses/UnprocessableEntityError"}}}}}}
```

## Delete a policy rule

> Removes a single named rule from the active configuration and creates a new configuration version.

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"servers":[{"url":"https://api.p0.app/o/{orgId}","variables":{"orgId":{"default":"demo-org","description":"The organization ID (your organization slug)."}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Token"}},"parameters":{"name":{"name":"name","in":"path","required":true,"description":"The policy rule's unique name. Cannot contain a forward slash (`/`) or backslash (`\\`). Names match case-insensitively, so `Engineering-AWS` and `engineering-aws` refer to the same rule.","schema":{"type":"string"}}},"responses":{"UnauthorizedError":{"description":"The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization, so it holds no P0 role. Add the identity — for example, a service account's email — as a member in Role-Based Access Control."},"ForbiddenError":{"description":"The token is valid and the identity is a member of your P0 organization, but it lacks the required permission — for example, a Manager calling a write endpoint, which requires the Owner role."},"NotFoundError":{"description":"No policy matches the given name or version ID."},"ConflictError":{"description":"The change conflicts with the current state — for example, a rule with the given name already exists, more than one rule matches the given name, or the supplied `currentVersion` no longer matches the active configuration."}}},"paths":{"/policy/name/{name}":{"delete":{"summary":"Delete a policy rule","description":"Removes a single named rule from the active configuration and creates a new configuration version.","parameters":[{"$ref":"#/components/parameters/name"}],"responses":{"204":{"description":"The rule was deleted."},"401":{"$ref":"#/components/responses/UnauthorizedError"},"403":{"$ref":"#/components/responses/ForbiddenError"},"404":{"$ref":"#/components/responses/NotFoundError"},"409":{"$ref":"#/components/responses/ConflictError"}}}}}}
```

## Policy schema

## The Policy object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}}}}
```

## The PolicyConfig object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"PolicyConfig":{"type":"object","description":"A complete policy configuration — the full set of rules that is active at once.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}},"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}}}}
```

## The PolicyConfigWithMeta object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"PolicyConfigWithMeta":{"allOf":[{"$ref":"#/components/schemas/PolicyConfig"},{"type":"object","description":"A stored policy configuration document, with its metadata.","properties":{"id":{"type":"string","description":"The document ID. For a version document this is its auto-generated version ID; for the active-configuration pointer it is the literal `latest`."},"version":{"type":"string","description":"The active configuration's version ID. Present when you read `latest`; use it as `currentVersion` when replacing the whole configuration."},"createdDate":{"type":"string","format":"date-time","description":"When this configuration document was last written."}}}]},"PolicyConfig":{"type":"object","description":"A complete policy configuration — the full set of rules that is active at once.","required":["rules"],"properties":{"rules":{"type":"array","items":{"$ref":"#/components/schemas/Policy"}}}},"Policy":{"type":"object","description":"A single access-control policy rule.","required":["requestor","resource","approval"],"properties":{"name":{"type":"string","description":"A unique, human-readable name for the rule. Cannot contain `/` or `\\`."},"disabled":{"type":"boolean","description":"If true, P0 skips this rule during evaluation. Defaults to false."},"requestor":{"$ref":"#/components/schemas/RequestorRule"},"resource":{"$ref":"#/components/schemas/ResourceRule"},"approval":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalRule"}}}},"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}},"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]},"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}}}}
```

## The RequestorRule object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"RequestorRule":{"description":"Matches who is making the request.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"$ref":"#/components/schemas/AgenticRequestorRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"UserRequestorRule":{"type":"object","description":"Matches a single user by email address.","required":["type","uid"],"properties":{"type":{"type":"string","enum":["user"]},"uid":{"type":"string","description":"The user's email address."}}},"AgenticRequestorRule":{"type":"object","description":"Matches requests made by AI agents through the P0 AI Gateway, based on the acting agent and the human user on whose behalf the agent acts.","required":["type","agent","user"],"properties":{"type":{"type":"string","enum":["agentic"]},"agent":{"description":"The agent half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"type":"object","required":["type","clientId"],"properties":{"type":{"type":"string","enum":["agent-client"]},"clientId":{"type":"string"}}},{"type":"object","required":["type","providerId"],"properties":{"type":{"type":"string","enum":["provider"]},"providerId":{"type":"string"},"subjectPattern":{"type":"string","description":"An optional regular expression matched against the federated subject."}}},{"type":"object","required":["type","owner"],"properties":{"type":{"type":"string","enum":["agent-owner"]},"owner":{"type":"string"}}},{"type":"object","required":["type","groups"],"properties":{"type":{"type":"string","enum":["owner-group"]},"groups":{"$ref":"#/components/schemas/GroupRule"}}}]},"user":{"description":"The human-user half of the match.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/GroupRule"},{"$ref":"#/components/schemas/UserRequestorRule"},{"type":"object","description":"Matches an agent-only session with no human user.","required":["type"],"properties":{"type":{"type":"string","enum":["none"]}}}]}}}}}}
```

## The ResourceRule object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"ResourceRule":{"description":"Matches which resource the request targets.","oneOf":[{"$ref":"#/components/schemas/AnyRule"},{"$ref":"#/components/schemas/IntegrationResourceRule"}],"discriminator":{"propertyName":"type"}},"AnyRule":{"type":"object","description":"Matches any requestor or any resource.","required":["type"],"properties":{"type":{"type":"string","enum":["any"]}}},"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]}}}}
```

## The ApprovalRule object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"ApprovalRule":{"description":"One entry in a policy's approval path. A policy's `approval` array can hold several rules; approval from any one of them provisions the request, except that a single `deny` rule denies all matching requests.","oneOf":[{"type":"object","description":"Routes to the organization's Security Reviewers.","required":["type"],"properties":{"type":{"type":"string","enum":["p0"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"allOf":[{"$ref":"#/components/schemas/GroupRule"},{"type":"object","description":"Routes to members of a directory group.","properties":{"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},{"type":"object","description":"Auto-approves if the requestor is currently on call.","required":["type","integration"],"properties":{"type":{"type":"string","enum":["auto"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"options":{"$ref":"#/components/schemas/ApprovalOptions"}}},{"type":"object","description":"Routes to on-call users for the listed services or schedules.","required":["type","integration","services"],"properties":{"type":{"type":"string","enum":["escalation"]},"integration":{"type":"string","enum":["pagerduty","incidentio"]},"services":{"type":"array","description":"PagerDuty service IDs or Incident.io schedule IDs.","items":{"type":"string"}},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Automatically grants access (standing access).","required":["type"],"properties":{"type":{"type":"string","enum":["persistent"]},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}},{"type":"object","description":"Denies all matching requests. Takes precedence over every other rule.","required":["type"],"properties":{"type":{"type":"string","enum":["deny"]}}},{"type":"object","description":"Routes to a user named by a property of the requestor's directory profile, such as their manager.","required":["type","directory"],"properties":{"type":{"type":"string","enum":["requestor-profile"]},"directory":{"$ref":"#/components/schemas/Directory"},"profileProperty":{"type":"string"},"options":{"$ref":"#/components/schemas/UserApprovalOptions"}}}]},"UserApprovalOptions":{"type":"object","description":"Options for approval rules that route to people.","properties":{"allowOneParty":{"type":"boolean","description":"If true, requestors can approve their own matching requests. Defaults to false."},"breakGlassApprover":{"type":"boolean","description":"If true, designates this approver as a break-glass approver for SSH \"all\" access. Defaults to false."},"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}},"GroupRule":{"type":"object","description":"Matches members of one or more directory groups.","required":["type","effect","groups"],"properties":{"type":{"type":"string","enum":["group"]},"effect":{"type":"string","description":"Whether membership of the listed groups includes (`keep`) or excludes (`remove`) the requestor.","enum":["keep","remove"]},"groups":{"type":"array","items":{"$ref":"#/components/schemas/IdpGroup"}}}},"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]},"ApprovalOptions":{"type":"object","description":"Options for automated approval rules.","properties":{"requireReason":{"type":"boolean","description":"If true, requestors must supply a justification. Defaults to false."},"requireDuration":{"type":"boolean"},"requirePreapproval":{"type":"boolean"}}}}}}
```

## The IntegrationResourceRule object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"IntegrationResourceRule":{"type":"object","description":"Matches resources in a specific integration.","required":["type","service"],"properties":{"type":{"type":"string","enum":["integration"]},"service":{"type":"string","description":"The integration the rule applies to, such as `aws`, `gcloud`, `azure`, `k8s`, `snowflake`, or `ssh`."},"accessType":{"type":"string","description":"The access type within the service the rule applies to, such as `role`, `permission-set`, or `resource`. Defaults to matching any access type when omitted."},"filters":{"type":"object","description":"Narrows the rule to resources that match a filter, keyed by the resource property being filtered (for example `policy` or `role`).","additionalProperties":{"oneOf":[{"$ref":"#/components/schemas/PatternFilter"},{"$ref":"#/components/schemas/BooleanFilter"}]}}}},"PatternFilter":{"description":"Includes or excludes resources whose property matches a regular expression. Patterns are unanchored; use `^` and `$` to anchor them.","oneOf":[{"type":"object","required":["effect","key","pattern"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"key":{"type":"string","description":"The resource property to match against, such as `arn` or `name`."},"pattern":{"type":"string","description":"A JavaScript regular expression matched against the property value."}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","description":"Disables the resource type entirely.","enum":["removeAll"]}}}]},"BooleanFilter":{"description":"Includes or excludes resources by a boolean property, such as SSH `sudo`.","oneOf":[{"type":"object","required":["effect","value"],"properties":{"effect":{"type":"string","enum":["keep","remove"]},"value":{"type":"boolean"}}},{"type":"object","required":["effect"],"properties":{"effect":{"type":"string","enum":["removeAll"]}}}]}}}}
```

## The IdpGroup object

```json
{"openapi":"3.0.4","info":{"title":"P0 Access Policies API","version":"1.0.0"},"components":{"schemas":{"IdpGroup":{"type":"object","description":"A single directory group.","required":["id","label","directory"],"properties":{"id":{"type":"string","description":"The group identifier. For Google Workspace, the group email address. For Okta, the group ID from the admin console URL. For Microsoft Entra ID, the group's UUID."},"label":{"type":"string","description":"A human-readable name for the group, shown in approval notifications."},"directory":{"$ref":"#/components/schemas/Directory"}}},"Directory":{"type":"string","description":"The directory provider a group belongs to.","enum":["azure-ad","entra-id","okta","workspace"]}}}}
```

A `Policy` has three required parts and two optional fields:

| Field       | Type             | Required | Description                                                                                                              |
| ----------- | ---------------- | -------- | ------------------------------------------------------------------------------------------------------------------------ |
| `name`      | string           | No       | A unique, human-readable name. Cannot contain `/` or `\`. Required to address the rule under `/policy/name/{name}`.      |
| `disabled`  | boolean          | No       | If `true`, P0 skips the rule during evaluation. Defaults to `false`.                                                     |
| `requestor` | `RequestorRule`  | Yes      | Matches who is making the request: `any`, `user`, `group`, or `agentic`.                                                 |
| `resource`  | `ResourceRule`   | Yes      | Matches the target: `any` or a specific `integration`.                                                                   |
| `approval`  | `ApprovalRule[]` | Yes      | The approval path. At least one non-break-glass approver is required. A single `deny` rule denies all matching requests. |

{% hint style="info" %}
The JSON here is the API's wire format. When you match a directory group, `requestor` and group approvers use a `groups` array with an `effect`, for example `{ "type": "group", "effect": "keep", "groups": [ ... ] }`. Policy objects reject unknown fields, so a mistyped or extra property returns `400 Bad Request`. For the complete rule reference — every requestor, resource, filter, and approval type, plus evaluation order — see the [Access Policies](/access-management/just-in-time-access/access-policies.md) guide.
{% endhint %}

## Examples

The following examples assume your bearer token is in the `P0_API_TOKEN` environment variable and your organization slug is `your-org`. See [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md) to get a token.

### Read a single rule

```bash
curl -H "Authorization: Bearer $P0_API_TOKEN" \
  https://api.p0.app/o/your-org/policy/name/engineering-aws-readonly
```

The response is the matching `Policy`:

```json
{
  "name": "engineering-aws-readonly",
  "requestor": {
    "type": "group",
    "effect": "keep",
    "groups": [
      { "id": "engineering@yourcompany.com", "label": "Engineering", "directory": "workspace" }
    ]
  },
  "resource": {
    "type": "integration",
    "service": "aws",
    "accessType": "permission-set",
    "filters": {
      "permission-set": { "effect": "remove", "key": "name", "pattern": "FullAccess|Admin" }
    }
  },
  "approval": [
    { "type": "p0", "options": { "requireReason": true } }
  ]
}
```

### Create a rule

Add a rule by `POST`ing a `Policy` to `/policy/name/{name}`. The `name` in the body must match the `name` in the URL. This example routes the engineering group's non-admin AWS permission-set requests to your Security Reviewers, and requires a reason:

```bash
curl -X POST \
  -H "Authorization: Bearer $P0_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "engineering-aws-readonly",
    "requestor": {
      "type": "group",
      "effect": "keep",
      "groups": [
        { "id": "engineering@yourcompany.com", "label": "Engineering", "directory": "workspace" }
      ]
    },
    "resource": {
      "type": "integration",
      "service": "aws",
      "accessType": "permission-set",
      "filters": {
        "permission-set": { "effect": "remove", "key": "name", "pattern": "FullAccess|Admin" }
      }
    },
    "approval": [
      { "type": "p0", "options": { "requireReason": true } }
    ]
  }' \
  https://api.p0.app/o/your-org/policy/name/engineering-aws-readonly
```

A successful create returns `201 Created` and echoes the stored rule. If a rule with that name already exists, the request fails with `409 Conflict` — use `PUT` to change it instead.

### Update a rule

`PUT` replaces an existing rule. This example changes the approver to the SRE directory group and lets on-call engineers self-approve:

```bash
curl -X PUT \
  -H "Authorization: Bearer $P0_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "engineering-aws-readonly",
    "requestor": {
      "type": "group",
      "effect": "keep",
      "groups": [
        { "id": "engineering@yourcompany.com", "label": "Engineering", "directory": "workspace" }
      ]
    },
    "resource": { "type": "integration", "service": "aws" },
    "approval": [
      {
        "type": "group",
        "effect": "keep",
        "groups": [
          { "id": "sre@yourcompany.com", "label": "SRE", "directory": "workspace" }
        ],
        "options": { "allowOneParty": true, "requireReason": true }
      }
    ]
  }' \
  https://api.p0.app/o/your-org/policy/name/engineering-aws-readonly
```

A successful update returns `200 OK` with the stored rule. If no rule with that name exists, the request fails with `404 Not Found` — use `POST` to create it.

### Delete a rule

```bash
curl -X DELETE \
  -H "Authorization: Bearer $P0_API_TOKEN" \
  https://api.p0.app/o/your-org/policy/name/engineering-aws-readonly
```

A successful delete returns `204 No Content` with an empty body.

### Read the active configuration

To read the active configuration with the `version` you need for a concurrent-safe write, get the special `latest` configuration:

```bash
curl -H "Authorization: Bearer $P0_API_TOKEN" \
  https://api.p0.app/o/your-org/policy/latest
```

The response wraps the active configuration, its `version`, and metadata:

```json
{
  "policy": {
    "id": "latest",
    "version": "8f3c1a2b",
    "createdDate": "2026-08-14T17:04:12.000Z",
    "rules": [
      {
        "name": "deny-gcloud-owner",
        "requestor": { "type": "any" },
        "resource": {
          "type": "integration",
          "service": "gcloud",
          "accessType": "role",
          "filters": { "role": { "effect": "keep", "key": "id", "pattern": "roles/owner" } }
        },
        "approval": [ { "type": "deny" } ]
      }
    ]
  }
}
```

{% hint style="info" %}
`GET /policy` (without `latest`) lists **every** stored document, not just versions, and in no particular order. It also returns the `latest` pointer (`id: "latest"`) and an internal options document (`id: "_default_"`, with no `rules`). Use `GET /policy/latest` to read the current configuration, and a specific `version` ID to read a past snapshot. Do not use a version ID from the list as `currentVersion` — the concurrency check compares against the version stored on `latest`.
{% endhint %}

### Replace the whole configuration

`POST /policy` replaces every rule at once and creates a new configuration version. To avoid overwriting a concurrent change, pass the `version` from `GET /policy/latest` as `currentVersion`. Submit the new configuration:

```bash
curl -X POST \
  -H "Authorization: Bearer $P0_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "currentVersion": "8f3c1a2b",
    "policy": {
      "rules": [
        {
          "name": "deny-gcloud-owner",
          "requestor": { "type": "any" },
          "resource": {
            "type": "integration",
            "service": "gcloud",
            "accessType": "role",
            "filters": { "role": { "effect": "keep", "key": "id", "pattern": "roles/owner" } }
          },
          "approval": [ { "type": "deny" } ]
        },
        {
          "name": "engineering-any",
          "requestor": {
            "type": "group",
            "effect": "keep",
            "groups": [
              { "id": "engineering@yourcompany.com", "label": "Engineering", "directory": "workspace" }
            ]
          },
          "resource": { "type": "any" },
          "approval": [ { "type": "p0" } ]
        }
      ]
    }
  }' \
  https://api.p0.app/o/your-org/policy
```

The response contains the stored configuration and the new `version`:

```json
{
  "policy": {
    "rules": [ ... ],
    "version": "b7e90d44"
  }
}
```

If the active configuration changed since you read `currentVersion`, the request fails with `409 Conflict` and the message *"Policies have changed since your edits. Please reload rules and apply these edits again."* Reload with `GET /policy/latest`, reapply your edits, and retry.

## Errors

| Status                     | Meaning                                                                                                                                                                                                                                                                                                                                        |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`          | The body failed schema validation — for example, the `name` contains a slash, the `name` in the body does not match the URL, `service` is not a known integration, a filter's `key` is invalid, or the body includes an unknown field. The response describes the problem.                                                                     |
| `401 Unauthorized`         | The token is missing or invalid, or it is valid but belongs to an identity that is not a member of your P0 organization (it holds no P0 role). Add the identity — for example, a service account's email — as a member in Role-Based Access Control. See [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md). |
| `403 Forbidden`            | The token is valid and the identity is a member, but it lacks the required permission — for example, a Manager calling a write endpoint.                                                                                                                                                                                                       |
| `404 Not Found`            | No rule matches the `name`, or no configuration matches the `policyId`.                                                                                                                                                                                                                                                                        |
| `409 Conflict`             | A rule with that `name` already exists (on create), more than one rule matches the `name` (on get, update, or delete), or the supplied `currentVersion` no longer matches the active configuration.                                                                                                                                            |
| `422 Unprocessable Entity` | A filter `pattern` (or an agentic `subjectPattern`) is not a valid regular expression, or is unsafe because it is subject to catastrophic backtracking.                                                                                                                                                                                        |

## Related

* [Access Policies](/access-management/just-in-time-access/access-policies.md) — the full rule reference: requestor, resource, filter, and approval types, plus evaluation order.
* [Configure your first access policy](/access-management/just-in-time-access/access-policies/configure-your-first-access-policy.md) — build a policy in Policy Studio.
* [Agentic access policies](/access-management/just-in-time-access/access-policies/agentic-access-policies.md) — the `agentic` requestor rule for AI agents.
* [Authenticating with the P0 API](/getting-started/authenticating-with-the-p0-api.md) — get a bearer token to authenticate these requests.
* [P0 API overview](/getting-started/p0-api-overview.md) — the base URL, authentication, and permissions shared by all P0 APIs.
