πRequest Routing
Request routing allows you to limit and direct just-in-time access requests, based on who is making the request and which resource is requested.
This page describes how to use and configure request routing rules.
Configuring Request Routing
To use request routing, go to p0.app and navigate to "Routing". Saving a routing configuration here will configure your organization to use routed approvals, rather than default approvals.

Request Routing Format
When working with Request Routing directly, you should use this reference page for specific definitions of how to format your request properly. This feature of p0 is powerful and customizable, and that's why we've made it accessible via a full API for those who want to use it that way.
Structure of a "Workflow"
We call a collection of routing rules a Workflow, and when you want to make changes to the way your requests are routed in p0 (such as, sending engineers' request to their managers, or restricting access to a particular prod resource to a small group), you do so by submitting a new Workflow.
Keep in mind that you only have one Workflow active at any given time, but that one Workflow can have as many individual routing rules as you want. You do not need to separate out different rule types or anything like that.
Here's an example Workflow that you can copy into your app as a starting point:
- requestor:
type: group
id: [email protected]
label: Engineering
directory: workspace
resource:
type: integration
service: snowflake
approval:
- type: group
id: [email protected]
label: Data Ops Team
directory: workspace
options: {allowOneParty: false}
Structure of a "Rule"
The core of workflows are the rules, so let's break them down.
Requestor
The first part of a rule is a "requestor", which matches who makes the request. There are three types of requestors you can choose from:
"Any"
requestor:
type: any
This will match all requestors. Useful for global rules like fall-back restrictions of sensitive resources.
"Group"
requestor:
type: group
id: <group identifier>
label: <human-readable name>
directory: azure-ad|okta|workspace
This rule will match any requestor that is a member of a group in your IdP. Currently, this supports Google Workspace, Microsoft Entra ID, and Okta.
id: For Google Workspace, this is the group email address (ie, [email protected]). For Entra, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See Okta docs.
label: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory.
directory: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID, this is "azure-ad".
"User"
requestor:
type: user
uid: <user's email address>
This rule will match only a specific user, as identified by their email address.
Resource
The second part of a routing rule is the resource this rule should apply to.
"Any"
resource:
type: any
This will match all resources. This is useful, for instance, when you want to route access for a particular user, regardless of what resource or service they are looking to access.
"Integration"
resource:
type: integration
service: <target service>
accessType <target access type>
This rule will match a specific type of access request. For instance, maybe you want to create a rule that routes all AWS requests to your DevOps team. Or you want to restrict access to GCP to only a select group.
service: For GCloud, "gcloud", for AWS, "aws", and for Snowflake, "snowflake".
accessType (Optional): The access type within the service that the rule should apply to, or "any" meaning the rule will match requests of any access type. Defaults to "any" if omitted.
filters (Optional): Filters allow you to apply the rule only to the service components that match the filtering condition.
resource:
type: integration
service: aws|azure-ad|gcloud|k8s|okta|snowflake
accessType: <access-type>
filters:
<filter-name>:
effect: keep|remove|removeAll
key: <property>
pattern: <regex pattern>
Each filter has an filter-name
that refers to the type of the requested object. For instance, when "service" is "aws", the filter with the name "policy" will only allow requesting policies whose ARNs match the pattern
.
Note that filters are independent when the access type is omitted or set to "any." For example, if you specify an filter-name=permission
filter for service=gcloud
requests, but omit a filter-name=role
filter, then roles will be unaffected by the filter, i.e. all roles will remain requestable.
Each filter has three potential properties:
effect - Describes how this filter is applied
keep - This rule will retain objects that match the pattern as requestable
remove - This rule will only retain objects that do not match the pattern
removeAll - This rule disables the object type entirely.
If you want to allow all objects of a type as requestable, omit the filter altogether
key - Describes which property of the object the filter must match; only applies if "effect" is one of "keep" or "remove". For instance, if
service=aws
andaccess-type=permission-set
then the key can be "name" or "arn". If "name", the regex pattern will be matched against the name of permission sets. If "arn", the entire arn is matched.pattern - A regex pattern used to match against the specified property; only applies if "effect" is one of "keep" or "remove"
β οΈ Note: that some filters have different effects on which requests are allowable in ways that are difficult to reason about. Therefore, we highly recommend setting the access type field to a specific access type rather than "any," especially when using filters.
For example, the "role" filter in the Gcloud integration has a slightly different meaning depending on which access type is being used used in the request:
When making an GCP request with "role" as the access type (e.g.
p0 request gcloud role
), the filter limits the roles the user can request access toWhen making an GCP request with "resource" as the access type (e.g.
p0 request gcloud resource
), the filter limits the roles that the user can request on the resource they are requesting
By setting the rule's access type to either "role" or "resource", you can explicitly specify which of these requests is allowable.
Valid filter filter-name / key combinations are summarized in the below table. For more detailed information on the filters, see the documentation for AWS and Google Cloud.
aws
tag
<tag key>
Only filters policies and permission sets. AWS managed policies cannot be tagged.
group
name
Filters IAM group names
permission-set
arn | name
Filters Identity Center permission sets
policy
arn
Filters policies with matching ARNs
resource
arn | name | service
Filters resource requests based on the resource ARN, the resource name, or the service the resource belongs to.
azure-ad|okta
group
id | label
Filters directory groups
gcloud
permission
id
Filters IAM permissions
role
id
Filters IAM roles based on full ID (e.g. the ID for "Owner" is roles/owner
)
resource
name | type | full-resource-name
Filters resource requests based on the resource name, resource type, or the full resource name.
snowflake
role
name
Filters roles
k8s
resource
kind | name | namespace
Filters resources
role
name
Filters roles and cluster roles
cluster
name
Filters clusters based on the cluster name specified when installed in P0
Note that filters are not currently available for AWS or GCP resource-level grants, nor for Snowflake SQL grants.
Example that covers each possible access-type in the AWS service:
Exclude all permission sets and policies containing "FullAccess"
Only allow AWS policies and permission sets where the tag "P0Grantable" is equal to the string "true"
Do not allow requesting any AWS groups
- resource
type: integration
service: aws
accessType: permission-set
filters:
permission-set:
effect: remove
key: name
pattern: FullAccess
tag:
effect: keep
key: P0Grantable
pattern: ^true$
requestor:
type: any
approval:
- type: any
- resource
type: integration
service: aws
accessType: policy
filters:
policy:
effect: remove
key: name
pattern: FullAccess
tag:
effect: keep
key: P0Grantable
pattern: ^true$
requestor:
type: any
approval:
- type: any
- resource
type: integration
service: aws
accessType: group
requestor:
type: any
approval:
- type: deny
Approval
The final part of a routing rule is the "approval". Unlike the requestor and resource parts, the approval part is an array of multiple rules, referring to the people/groups/services that can approve (or deny) access requests.
"p0"
approval:
- type: p0
options: { allowOneParty: true|false, requireReason: true|false }
This approval type is analogous to the default approval behavior. This rule routes approvals to the people designated as "Approvers" on the Settings page of your app. The "options" key is optional. You can use it to:
allow the requestor to approve their own requests for this flow with the
allowOneParty: true
setting. Defaults tofalse
.require the requestor to specify a reason when submitting requests with the
requireReason: true
setting. Defaults tofalse
.
"Group"
approval:
- type: group
id: <group identifier>
label: <human readable name>
directory: azure-ad|okta|workspace
options: { allowOneParty: true|false, requireReason: true|false }
This rule will match any requestor who is a member of a group in your Identity Provider. Currently, p0 supports Google Workspace, Microsoft Entra ID, and Okta.
id: For Google Workspace, this is the group email address (ie, [email protected]). For Entra ID, this is the Entra ID group's UUID. For Okta, this is the group ID found in the URL of the group's page in the admin console. See Okta docs.
label: This is any friendly human-readable name you like, although P0 suggests using the same name as displayed in your directory. This label will be printed in approval notifications.
directory: For Google Workspace this is "workspace", for Okta this is "okta", and for Entra ID this is "azure-ad".
options: See "p0" section
"Auto"
approval:
- type: auto
integration: pagerduty
options: { requireReason: true|false }
This rule automatically approves matching access requests for 1 hour.
integration: currently only "pagerduty" is supported. If the requestors in on-call in PagerDuty their requests will be automatically approved.
options: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the requireReason: true
setting. Defaults to false
.
"Escalation"
approval:
- type: escalation
integration: pagerduty
options: { allowOneParty: true|false, requireReason: true|false }
services: [<PagerDuty Service ID>]
This rule allows requests to be escalated by creating incident against multiple PagerDuty services using escalate button presented to the requestor. This rule needs to be used along with other approval rules if approval is needed even before the request is escalated.
Integration: currently only "pagerduty" is supported. If the approver is on-call based on the escalation policy of the configured services and the request is escalated, the request can either be approved or denied by the approver.
options: See "p0" section
services: List of PagerDuty service ids against which the incident needs to be triggered.
"Always allowed"
approval:
- type: persistent
Access is always granted automatically.
options: The "options" key is optional. You can use it to require the requestor to specify a reason when submitting requests with the requireReason: true
setting. Defaults to false
.
"Deny"
approval:
- type: deny
This rule will deny all requests that match. This supersedes other matching rules: if at least one "deny" rule matches, the request will be denied.
Examples
Routing rules can be used for a variety of use cases.
Different access depending on organizational role
Configure different approvers and different resources for developers and customer-success engineers:
- requestor:
type: group
id: [email protected]
label: Developers
directory: workspace
resource:
type: any
approval:
- type: group
id: [email protected]
label: SREs
directory: workspace
options: { allowOneParty: true }
- requestor:
type: group
id: [email protected]
label: Customer Success
directory: workspace
resource:
type: integration
service: snowflake
approval:
- type: group
id: [email protected]
label: Data Ops
directory: workspace
options: { allowOneParty: false }
Different rules by AWS account
Development AWS account (123456789) allows one-party approvals. Staging AWS account (234567891) allows peer approvals but no one-party approvals. Production AWS account (345678912) requires a manager approver represented by an Okta group. Assumes an AWS Identity Center setup where resource filters allow requesting customer-managed policies in specific AWS accounts, enforced by an ARN match that includes the AWS account ID. Permission sets are implicitly allowed because the permission-set
access type filter is not defined.
- requestor:
type: group
id: 00g5j4jojlGZMzfhM69
label: Engineers
directory: okta
resource:
type: integration
service: aws
accessType: resource
filters:
policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::123456789:policy/ }
group: { effect: removeAll }
approval:
- type: group
id: 00g5j4jojlGZMzfhM69
label: Engineers
directory: okta
options: { allowOneParty: true }
- requestor:
type: group
id: 00g5j4jojlGZMzfhM69
label: Engineers
directory: okta
resource:
type: integration
service: aws
accessType: resource
filters:
policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::234567891:policy/ }
group: { effect: removeAll }
approval:
- type: group
id: 00g5j4jojlGZMzfhM69
label: Engineers
directory: okta
options: { allowOneParty: false, requireReason: true }
- requestor:
type: group
id: 00g5j4jojlGZMzfhM69
label: Engineers
directory: okta
resource:
type: integration
service: aws
accessType: resource
filters:
policy: { effect: keep, key: arn, pattern: ^arn:aws:iam::345678912:policy/ }
group: { effect: removeAll }
approval:
- type: group
id: 01f5j4jfjlGZMzfhN99
label: Managers
directory: okta
options: { allowOneParty: false, requireReason: true }
Filter access to AWS policies and permission sets by tag
Restrict which AWS policies and permission sets can be requested to those for which a tag matches a specific value:
- requestor:
type: any
resource:
type: integration
service: aws
accessType: policy
filters:
tag: { effect: keep, key: P0Grantable, pattern: ^true$ }
approval:
- type: p0
- requestor:
type: any
resource:
type: integration
service: aws
accessType: permission-set
filters:
tag: { effect: keep, key: P0Grantable, pattern: ^true$ }
approval:
- type: p0
Org- and resource-specific routing
Allow either the requestor's manager or the service owner to approve requests:
- requestor:
type: group
group: [email protected]
directory: workspace
resource:
type: any
approval:
- type: group
id: [email protected]
label: Eng managers
directory: workspace
- requestor:
type: group
group: [email protected]
directory: workspace
resource:
type: integration
service: gcloud
approval:
- type: group
id: [email protected]
label: GCloud owners
directory: workspace
Mandatory reason for a specific resource type
Make reason mandatory for AWS requests
- resource:
type: any
approval:
- options: {requireReason: false, allowOneParty: true}
type: p0
requestor:
type: any
- resource:
service: aws
type: integration
approval:
- options: {requireReason: true}
integration: pagerduty
type: auto
requestor:
type: any
Exclude cluster-admin role and only allow default namespace in Kubernetes
- resource:
type: integration
service: k8s
accessType: resource
filters:
resource:
effect: keep
key: namespace
pattern: ^default$
role:
effect: remove
key: name
pattern: ClusterRole/cluster-admin
approval:
- type: p0
requestor:
type: any
Escalate request using pagerduty for priority approval when requesting google cloud requests
- resource:
service: gcloud
type: integration
approval:
- type: p0
options: {requireReason: true, allowOneParty: false}
- type: escalation
integration: pagerduty
options: {requireReason: true, allowOneParty: false}
services: [PSJXXXG]
requestor:
type: any
Allow users to request access to Google Cloud roles, but not permissions
- resource:
service: gcloud
type: integration
accessType: role
approval:
- type: p0
requestor:
type: any
- resource:
service: gcloud
type: integration
accessType: permission
approval:
- type: deny
requestor:
type: any
Deny access to specific roles, but allow users to request others
- resource:
service: gcloud
type: integration
accessType: role
filters:
role:
effect: keep
key: name
pattern: roles/owner
approval:
- type: deny
requestor:
type: any
- resource:
service: gcloud
type: integration
accessType: role
approval:
- type: p0
requestor:
type: any
Evaluation of Routing Rules
Match Rules to the Request
Identify rules that match the request. A rule matches if both the requestor and the resource criteria in the rule align with the request details.
Handle Missing Matches
If no rules match, the access request is not created, and further evaluation stops. The requestor will see the following message:
This resource doesn't exist, or your organization doesn't allow this principal to access this resource
Evaluate Access Control of Matching Rules
For matching rules, review the Access Control field in the following order:
Deny all access If any matching rule specifies "Deny all access", the request is denied immediately, and further evaluation stops.
Always allowed If any rule specifies "Always allowed," the request is automatically approved and provisioned, and further evaluation stops.
Third-party auto-approval If a rule specifies a third-party auto-approval, such as a PagerDuty on-call rule, the request is evaluated for automatic approval. If it cannot be auto-approved, proceed to the next step.
Manual approval For all other Access Control values, the request requires manual approval by a designated person or group. When multiple rules with different manual approvers match, approval from any one approver is sufficient to provision the request. All approvers are notified.
Key Points to Remember
Deny all access: Always takes precedence and stops further evaluation immediately.
Always allowed: Overrides other Access Control values and is prioritized in the evaluation process.
Multiple matching rules: When multiple rules match the requestor and resource, approvers are combined using an ANY relationship, meaning approval from any one approver is sufficient.
Examples
Always allowed rule takes precedence over other allow rules

Consider a scenario where two rules apply to the same resource, node1
in the devstack group:
An "Always allow" rule grants access to
node1
.Another "Allow" rule requires approval before granting access.
In this case, the "Always allow" rule takes precedence. As a result:
The user is immediately granted access to
node1
without requiring additional approval.
Deny Rules are evaluated first, before ANY allow rules

Using the same example as before, if there is an additional rule that denies access to any instance containing node
in its name, the system will evaluate the "Deny" rule first.
In this case:
The "Deny" rule matches because
node1
contains the stringnode
in its name.The user's request is immediately denied, and is not evaluated further.
This highlights the priority of "Deny" rules, ensuring they are enforced before any other rules are considered.
Last updated